JS只有函数作用域,没有块级作用域。于是,聪明的程序猿们想出来利用函数作用域模仿块
级作用域的“锦囊妙计”,并被广泛使用:
(function (value) {
alert(value);
}('hello'));
(function (value) {
alert(value);
})('hello');
! function (value) {
alert(value);
} ('world');
+ function (value) {
alert(value);
} ('world');
- function (value) {
alert(value);
} ('world');
但上面千奇百怪的符号往往令初学者无比困惑。百度到的答案令人遗憾,很多都是曲解,(一个排名很靠前的博客把问题总结为:函数声明、函数表达式、匿名函数的区别,认为函数表达式后面可以加括号立即调用该函数,函数声明不可以。这根本没有理解本质所在)
这也让我在很长的一段时间里对立即执行函数感到迷惘。然而其实在《javascript精粹》和《JavaScript权威指南》中说的很清楚,我写这篇文章的目的所在,正是澄清这个问题。
首先,我们要明确的是,这些写法的目的都是相同的,立即执行里面的匿名函数(以达到闭包等等“不可告人的目的”)
为了解开其中的奥秘,首先让我们明确这些结构的组成部分——表达式
表达式
(1) 函数表达式
函数表达式有点类似于“函数直接量”,其中[]表示可选。
function [funName] () {
// ... ...
}
函数表达式还可以赋给某个变量
var sayHello = function () { ... ... }
(2) 调用表达式
语法:
函数表达式()
例如:
sayHello('Tom')
这样,会执行sayHello这个函数。也就是说,在函数后面加上()并传递相应的参数就可以执行在函数里定义的语句。
JS的原则
立即调用的函数不需要函数名,直接调用即可,所以很多“聪明人”的代码出现了下面的错误:
function (value) {
alert(value);
}('hello')
报:Unexpected token (
错误。错误的原因不在于聪明的程序猿们,因为这的确符合调用表达式的语法。其实,问题的本质在于JS的一项“霸王条款”:
一个语句不能以一个函数表达式开头,因为官方语法假定以单词function开头的语句是一个function语句
(《javascript精粹》114页)
这下答案揭晓了:上面立即调用函数句式的原因正在于此。JS引擎认为以function开头的是函数声明语句,不能是函数调用语句。
下面的代码没有问题,因为语句的最前面并非function:
var index = function () {
alert('函数表达式执行了!!');
return 666
} ();
index // =>666
解决问题
解决问题的最简单办法就是不让function出现在语句的最前面。()这个“物美价廉,绿色无公害”的运算符遍成了首选,所以立即执行函数的最常见形式是:
(function (value) {
alert(value);
}('hello'));
相对而言,!等运算符也行有副作用,而且结果往往令人大吃一惊:
var index = !function () {
alert('匿名函数执行了!!');
return 666
} ();
index // =>false
index变为fasle的原因是!为666做了强制转换并取了反。
大师Douglas Crockford的 javascript:the good part (《javascript精粹》)真是一本javascript的绝世秘籍,令人由衷赞叹!!