JavaScript匿名函数立即调用/执行(表达式 vs 声明)

64

可能是重复的问题:
JavaScript中函数表达式与声明的区别是什么?
解释JavaScript封装的匿名函数语法

为什么要这样做:

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

以及这个:

var f = function () {
    //code
}();

这段代码有效,而这段代码:

function () {
    //code
}();

看起来不一样吗?它们完全相同-定义了匿名函数并立即调用。有人能引用JavaScript/ECMAScript标准中解释这一点的语句吗?

更新:感谢大家的回答!因此,这涉及到函数表达式 vs. 函数声明。请参见这个Stack Overflow答案ECMAScript标准第13节以及这篇很棒的文章:具名函数表达式的神秘化解

总结答案:

  1. 第一个代码段被解释为表达式,因为应用了分组运算符()——请参见ECMAScript标准第11.1.6节。

  2. 在第二个代码段中,该函数被解释为表达式,因为它位于赋值运算符=的右侧。

  3. 第三个代码段没有任何东西可以让解释器将该函数视为表达式,因此它被视为声明,而声明没有标识符是无效的(Gecko允许它通过,但它在后面遇到()分组运算符时会出错(因为它认为正在应用于无效内容))。


2
什么是Javascript中函数表达式和声明的区别? - Felix Kling
true; 可能是一些临时的故障。 - lxa
1
你的第一个例子也不会起作用。结尾应该是 })() 而不是 **}())**。 - yoavmatchulsky
2
@yoavmatchulsky 第一个例子没问题。外层括号将其变成了函数表达式,所以两种方式都可以。 - Matthew Crumley
2
参见:解释JavaScript的封装匿名函数语法 - Christian C. Salvadó
显示剩余3条评论
4个回答

22
前两种情况展示了函数表达式,并且可以出现在与(1+1x*f(4))类似的任何表达式中。就像1+1会计算为2一样,这些表达式会计算为相应的函数。
第三种情况是一个函数声明语句,可以出现在其他语句(如ifwhile语句)可以出现的任何地方。
尝试通过函数声明语句声明匿名函数没有什么意义,因为之后没有人会得到对该函数的引用。
你需要在开头加上(var x =,就像前两种情况一样,是因为它们强制解析下一个部分以表达式上下文进行解析。(只需想象一下不能做var x = if ...,例如)。如果您只将function放在第一位,它将被解析为您不想要的声明语句

函数声明语句不能声明匿名函数,否则之后没有人能够引用该函数。实际上可以这样尝试:function () { }(但是它确实是无用的,因为它既不能在原地调用,也不能以其他方式调用)。 - lxa
更新:function() {}(即匿名声明)在Gecko中可以工作,但在WebKit中不行。 - lxa
@Ixa:无论如何,我已经编辑了这篇帖子,使其不再模棱两可 :) - hugomg

4

前两个是称为函数表达式的东西,意味着它是内联的,并且随着JS代码运行而解释。

第三个是函数声明,当代码编译时被解释。由于它在编译时被解释,因此您无法立即运行它,因为它周围的其他代码尚未运行。

举个例子:

// foo == undefined
// bar == function

function bar(){ .. }
var foo = function(){ ... }

// foo == function
// bar == function

简单来说,如果你在代码中看到 function 这个词没有任何前缀,那么它就是一个声明。如果有其他东西在它前面,那么它就是一个表达式。

2
@PeterMortensen -- 什么鬼?不要对随意的帖子进行毫无意义的编辑。 - user578895

1
这里有一个简单的思路:如果function是行首的第一个关键字,解析器将会把该行剩余部分解释为函数声明。换句话说,它会认为你试图写出像这样的东西,好像你忘记给你的函数命名了:
function foo(){ 
    // code
}

解决这个问题的方法是将整个函数包装在括号中,或将其作为变量赋值的一部分。在任何情况下,您都将function放在行的后面,并允许解析器识别您不是在编写函数声明。

对我来说,允许function出现在一行的开头并仍然区分函数表达式和函数声明似乎有点微不足道,但我想当JavaScript首次设计时,这并不是那么微不足道。


@Ixa: 确实。或许更加一致,但是这样做是否有益处值得怀疑,为什么要让语法变得复杂呢? - Lightness Races in Orbit
@Tomalak Geret'kal,我个人认为语言设计者从来没有预料到自执行匿名函数,所以我在这里不认为有任何问题。但我认为他们通过要求您在括号中包装自执行匿名函数使语法更加复杂。您使用 for 遍历数组和对象(而不是创建 foreach)的事实,也表明了他们可能在这里使用的灵活性。为什么它在语法上与声明可区分时就会引发异常。这只是让我困扰的地方。 - Andrew
@Tomalak Geret'kal,听起来你不喜欢这种风格。虽然我不明白这与问题有什么关系,但我对此没有意见。但是,如果按照一种方式会引发语法错误,那么有人在这里需要做些什么。此外,现在我已经阅读了ECMA 262的相关部分,实际上我找不到任何地方说您不能以函数表达式开头一行。因此,正如@Ixa所观察到的那样,这实际上是一个浏览器实现问题,而不是JS语法问题。 - Andrew
@Andrew:这是从语法上固有的。如果这个语法产生一个函数声明,那么它就不能产生一个函数表达式。 - Lightness Races in Orbit
@Tomalak Geret'ka,那为什么在FF中可以工作呢?请阅读ECMA规范。function(){/*code*/}是一个表达式。function identifier(){}是一个声明。同样,for(exp1;exp2;exp3){/*code*/}迭代数组的元素。for(exp) {/*code*/}迭代对象的成员。这里没有歧义,因为语句具有不同的签名。 - Andrew
显示剩余3条评论

0

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