这些自执行的匿名函数(也称为IIFE)实现之间有什么区别?

9

在许多书籍/博客文章中,自执行匿名函数模式被写成这样:

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

然而,在此运行JSLint会出现以下错误:

将调用移入包含函数的括号中。

例如,将其更改为以下内容即可正常工作:

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

问题

  1. 为什么第一个实现对于JSLint来说不够好?它们有什么区别?
  2. 什么是首选形式?JSLint总是正确的吗?
  3. 它为什么能工作?毕竟,function(){}()会抛出SyntaxError: Unexpected token (。但用括号包裹它突然就可以工作了?例如:(function(){}()) - 可以正常工作
    (毕竟这是JavaScript,而不是Lisp,那么在否则会产生语法错误的情况下添加括号的效果是什么?)

编辑:这在某种程度上是对此的后续(虽然我不会说完全重复):JSLint error: "Move the invocation into the parens that contain the function",因此我的主要问题是第3个问题,为什么它会工作?


2
很多很多的重复。简而言之,1-2:因为Crockford更喜欢这样。JSHint的创建只是因为JSLint太有偏见了。3. 因为对于函数表达式来说,标识符是可选的,而对于函数声明来说则是必须的(括号使其成为函数表达式)。请参阅http://kangax.github.io/nfe/#expr-vs-decl。 - Fabrício Matté
3
@user2246674 你是不是把 H 打成了 L?:P - Fabrício Matté
1
可能存在重复:https://dev59.com/UG865IYBdhLWcg3wfuwk https://dev59.com/BXA75IYBdhLWcg3wRW2c https://dev59.com/oXRC5IYBdhLWcg3wD8xV https://dev59.com/c2025IYBdhLWcg3wjGtO (我仍然没有找到我要找的那一个...) - Fabrício Matté
1
@FabrícioMatté 是的,是的,我做了。 - user2246674
@FabrícioMatté - 在Sublime Text 2中,JS Formatter有一个JSLint友好选项(默认关闭),再加上尾随空格插件,使得遵循JSLint几乎可以忍受。 :) - Eran Medan
显示剩余7条评论
2个回答

6

我不知道Crockford的观点是如何形成的,但我可以解释为什么在括号中包含函数可行。

在JavaScript中,function() { ... } 语法可以代表两种不同的东西:函数声明或函数表达式。

函数声明是一个语句,它在当前作用域下定义了指定名称的函数。

function example() { 
    alert("Hello World");
}

example();

函数表达式是一个表达式,它评估为一个新的Function实例。

var secondExample = function example() {
    alert("Hello World");
};

secondExample();
example(); // <-- throws an Error: example is not defined.

无论语法的出现是函数声明还是函数语句,取决于解析器的预期。JavaScript的解析器很简单,不会向前查找并注意到函数后面跟着(),因此应将其视为表达式。它只看到在一行开头的function,因此将其视为语句,当后面跟着()时就会导致语法错误。当您将其包装在括号中时,解析器将期望一个表达式,这样就可以正常工作了。

将其包装在括号中(无论放置在何处)是最清晰的方法,但任何使解析器期望表达式的方法都可以使用。例如,按位非运算符~

~function() {
    alert("Hello World");
}();

两种模式都不是函数声明,虽然这解决了问题3,但它真的离题了。 - user2246674
1
我不明白第一个为什么不是FunctionDeclaration。根据规范,FunctionDeclaration的定义是function Identifier ( FormalParameterListopt ) { FunctionBody },而我的例子符合这个定义。 - Jeremy
1
(function() {})()(第一种形式)与 (function() {}())(第二种形式)与 function(){}()(问题#3)的区别。当然,问题#3未能解决上下文,因为 x = function(){}() 再次是有效的(而不是FunctionDeclaration)。 - user2246674
1
哦,你是说问题中的示例。那是真的,但这种区别是需要解释(3)的。 - Jeremy
请注意,有其他的方式可以强制将函数解释为表达式,例如 _!function(){}();_。 - nnnnnn
非常好的解释,谢谢!这可能是一个重复的问题,但其他人没有像这样解释得那么好的答案(或者我看得太快了)。从解析器的角度来看,确实是一个有趣的解释。 - Eran Medan

1

1.

显然这是一个约定问题。第一个示例显示了“缺乏约定”(source)。实际上,两个示例都可以执行而不出错。

2.

在这种情况下,我更喜欢你的第一个示例,但这只是我的约定。JSLint通常是正确的。因此,如果您想遵循他们的命名约定,则当它基于约定显示警告时,符合约定是有意义的。

3.

这起作用是因为将function(){}包装在()中使其成为一个表达式,一旦与最后一个()配对,就立即调用它。因此,您拥有了一个立即调用的函数表达式。


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