自动执行匿名JavaScript函数的括号位置在哪里?

113

最近我在比较我项目中的版本和json2.js的当前版本,发现了一个有关函数表达式创建和自执行方式的差异。

现在的代码将匿名函数用括号包裹并执行,

(function () {
  // code here
})();

但现在它将自动执行的函数用括号括起来。

(function () {
  // code here
}());

解释JavaScript封装的匿名函数语法的被接受答案中,CMS发表了一条评论,“(function(){})();(function(){}());都是有效的。”

我想知道它们之间的区别?前者是否通过保留全局匿名函数来占用内存?括号应该放在哪里?


相关:立即函数调用语法(在JSLint中) - Bergi
1
还可以阅读关于这个结构的目的或者查看一个(技术性的) 解释 (也可以在这里找到)。关于为什么需要括号,请参见这个问题 - Bergi
4个回答

67

它们几乎相同。

第一个用括号在函数周围包裹起来以使其成为有效表达式,并调用它。表达式的结果未定义。

第二个执行该函数,并且自动调用周围的括号使其成为有效表达式。它也评估为未定义。

我认为没有一种“正确”的方法,因为表达式的结果是相同的。

> function(){}()
SyntaxError: Unexpected token (
> (function(){})()
undefined
> (function(){return 'foo'})()
"foo"
> (function(){ return 'foo'}())
"foo"

8
JSLint 希望写成 "(function(){}());"。JSLint 表示:"将调用移动到包含函数的括号中。" - XP1
28
实际上,你不仅限于这两种方法,你可以使用几乎任何让编译器意识到函数是表达式而不是语句的方式,例如 +function(){}()!function(){}() - Tgr
50
JSLint需要很多特定于Crockford的风格而非实质内容的东西,这就是其中之一。 - T.J. Crowder
4
“@ThorpeObazee:这实际上并不重要,所以你可以自己选择。我建议不要使用一些更奇特的写法 (-function(){}();!function(){}(); 或者其他任何在 function 前面的运算符也可以,但是我会坚持使用带括号的版本)。我看到第一个写法比第二个写法要多得多,并且这是我更喜欢的方式;这种方式对我来说更有意义,但这是主观的。值得一提的是:http://jsbin.com/ejaqow。” - T.J. Crowder
这里有一个详细的描述,为什么将调用移动到内部会更好:https://medium.com/airbnb-engineering/immediately-invoked-function-expressions-and-parentheses-eeea53b39e0b - norlin
显示剩余4条评论

13

在这种情况下,这并不重要。在第一个例子中,您调用解析为函数的表达式,并在第二个示例中定义并立即调用函数。它们很相似,因为第一个示例中的函数表达式只是函数定义。

还有其他更明显有用的情况可以调用解析为函数的表达式:

(foo || bar)()

3
为了对其他读者进行澄清(主要是因为我自己一开始也没有理解 :)),foo和/或bar必须已经等于某个函数。(例如 foo = function(){alert('hi');})。如果它们都不是函数,就会抛出一个错误。 - Alexander Bird
3
进一步澄清 - 如果 foo 是“真值”但不是函数,它也会抛出错误。 - JLRishe

11

除了语法之外,没有任何区别。

关于你对第二种方法的担忧:

考虑一下:

(function namedfunc () { ... }())

namedfunc即使你提供了名称,它仍然不会在全局范围内。对于匿名函数也是如此。将其置于该范围中的唯一方法是在括号内部将其赋值给一个变量。

((namedfunc = function namedfunc () { ... })())

外层括号是不必要的:

(namedfunc = function namedfunc () { ... })()

但你本来也不想要那个全局声明,对吧?

所以问题归结为:

(function namedfunc () { ... })()

你甚至可以进一步缩减它:名称是不必要的,因为它永远不会被使用(除非您的函数是递归的...即使是这样,您也可以使用arguments.callee

(function () { ... })()

这就是我对此事的看法(可能不正确,因为我还没有阅读ECMAScript规范)。希望能帮到你。


请注意,arguments.callee自ES5以来已被弃用(在严格模式下被禁止)。 - alexia
外部括号是不必要的:我认为它们可以防止在文件连接时出现错误,否则你可能需要一个!或其他东西。 - Jimmyt1988

-2

这种差异只是因为Douglas Crockford不喜欢IIFEs的第一种风格!(说真的)你可以在这个视频中看到!!

额外包装(){在两种风格中}的唯一原因是帮助使代码部分成为函数表达式,因为函数声明不能立即调用。一些脚本/缩小器只使用+!-~代替两个括号。像这样:

+function() {  
    var foo = 'bar';  
}();

!function() {  
    var foo = 'bar';  
}();

-function() {  
    var foo = 'bar';  
}();

~function() {  
    var foo = 'bar';  
}();

所有这些选择都与您的替代方案完全相同。在这些情况中进行选择完全由您决定,不会有任何区别。{ 带有()的那些会生成1字节较大的文件;-) }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接