与Michael Geary 的回答相反,赋值语句不需要分号。
考虑不起眼的立即调用函数表达式:
(function() { /* do something */ })();
包裹function() { ... }
for 的额外括号是什么?他们是为了防止它被解释为一个函数定义,强制它被解释为一个函数表达式。它基本上等同于这个,但没有创建另一个变量:
var temp = function() { /* do something */ };
temp();
但是括号只是引入必须是表达式的东西的一种方式。特别是,赋值语句的初始化器将引入表达式1:
someVar = /* what goes here must be an expression */;
显然,组合这些概念表明:
someVar = (function() { return 5; })();
实际上不需要环绕括号!我们可以很容易地写这个,因为function()
不能解释为函数定义:
someVar = function() { return 5; } ();
那么这和分号有什么关系呢?事实证明,如果下一行可以继续该语句或表达式,它将继续。2你写了这个:
that.baz = function() {}
(function() {
that.baz();
}());
有了这些知识,您就知道它实际上被解释为:
that.baz = (function() {})(function() {
that.baz();
}());
这有点棘手,所以这就是发生的事情:
- 首先,
function() {}
被评估(不执行),产生一个函数对象。
- 然后调用此函数对象,但请稍等!在我们开始正确执行它之前,它有一些我们必须评估的参数。
唯一的参数是表达式
function() {
that.baz();
}()
这显然是一个立即调用的函数表达式,所以我们将开始执行它。
- 我们尝试查找
that.baz
并调用它,但它失败了,因为that.baz
它不存在,因此解析为undefined
! 不好了!JavaScript 引擎在这里停止,因为抛出了一个未捕获的异常,但为了清楚起见,我们假设它继续。
- 我们从来没有从 中返回任何东西
function() { that.baz(); }
,所以 to 的论点function() {}
是undefined
。
- 我们现在执行
function() {}
正确。它从不绑定它的参数,所以undefined
忽略了。它不返回任何内容,因此调用结果为undefined
.
- 现在我们已经完成了对该表达式的评估!我们将设置
that.baz
结果,undefined
。
希望您现在看到解析器将您立即调用的函数表达式误解为另一个匿名函数的参数,立即调用它。
如何预防?
如前所述,括号并不是明确引入表达式的唯一方法。事实上,当它们无意中出现在表达式的中间时,可能会导致这样的问题。我们还提到了分配,但我们不想分配一些东西。
还剩下什么?一元运算符。我们可以使用一些。我喜欢!
,所以我们将使用它。而不是使用:
(function() {
that.baz();
}());
我们用:
!function() {
that.baz();
}();
!
只能出现在表达式的开头,因此 JavaScript 开始解析表达式。之后只能有一个表达式!
,因此函数被解析为函数表达式而不是函数定义。由于优先级,函数首先被调用。它不返回任何东西,所以它返回undefined
。undefined
是假的,所以!
翻转它并产生true
. 因为我们从来没有对这个表达式的结果做任何事情,所以true
被丢弃了。(如果我们返回一个真实的值也没关系;然后!
会产生false
并且会被丢弃。无论如何,它都会被丢弃。)
TLDR:对 IIFE 使用一元运算符而不是括号。
脚注
1 从技术上讲,赋值是一个表达式,而不是一个语句,但对于我们的目的而言,这并不重要。
2除了return
, continue
, break
, 和类似的陈述,它会造成更多的混乱。