解释封装的匿名函数语法

概要

你能解释JavaScript的封装匿名函数的语法背后的原因吗? 为什么这个工作: (function(){})(); 但是这不: function(){}();


我知道的

在JavaScript中,创build一个像这样的命名函数:

 function twoPlusTwo(){ alert(2 + 2); } twoPlusTwo(); 

您也可以创build一个匿名函数并将其分配给一个variables:

 var twoPlusTwo = function(){ alert(2 + 2); }; twoPlusTwo(); 

您可以通过创build一个匿名函数来封装一段代码,然后将其封装在括号中并立即执行:

 (function(){ alert(2 + 2); })(); 

这在创build模块化脚本时非常有用,以避免混淆当前范围或全局范围,并且可能存在冲突的variables – 例如Greasemonkey脚本,jQuery插件等。

现在,我明白为什么这个工作。 括号内包含的内容,只公开结果(我敢肯定有一个更好的方式来描述),如(2 + 2) === 4


我不明白

但是我不明白为什么这不起作用:

 function(){ alert(2 + 2); }(); 

你能解释一下吗?

它不起作用,因为它被parsing为FunctionDeclaration ,函数声明的名称标识符是必需的

当用圆括号括起来时,它被评估为FunctionExpression ,并且函数expression式可以被命名或不被命名。

FunctionDeclaration的语法如下所示:

 function Identifier ( FormalParameterListopt ) { FunctionBody } 

FunctionExpression s:

 function Identifieropt ( FormalParameterListopt ) { FunctionBody } 

正如你所看到的, FunctionExpressionIdentifier (Identifier opt )标记是可选的,因此我们可以有一个没有定义名字的函数expression式:

 (function () { alert(2 + 2); }()); 

命名的函数expression式:

 (function foo() { alert(2 + 2); }()); 

圆括号(正式称为分组操作符 )只能包围expression式,并对函数expression式进行评估。

这两个语法产物可能是模棱两可的,而且看起来完全一样,例如:

 function foo () {} // FunctionDeclaration 0,function foo () {} // FunctionExpression 

parsing器知道它是一个FunctionDeclaration还是一个FunctionExpression ,取决于它出现的上下文

在上面的例子中,第二个是expression式,因为逗号操作符也可以只处理expression式。

另一方面, FunctionDeclaration实际上只能出现在所谓的“ Program ”代码中,即全局范围外的代码,以及其他函数的FunctionBody体内。

应该避免块内部的函数,因为它们可能导致不可预知的行为,例如:

 if (true) { function foo () { alert('true'); } } else { function foo () { alert('false!'); } } foo(); // true? false? why? 

上面的代码实际上应该产生一个SyntaxError ,因为一个Block只能包含语句(而且ECMAScript规范没有定义任何函数语句),但是大多数的实现都是宽容的,并且只需要第二个函数, 'false!'

Mozilla实现-Rhino,SpiderMonkey – 具有不同的行为。 他们的语法包含一个非标准的函数声明,这意味着函数将在运行时进行评估,而不是在parsing时间,正如函数声明中所发生的那样。 在那些实现中,我们将获得定义的第一个函数。


函数可以用不同的方式声明, 比较如下 :

1- 函数构造函数定义的函数分配给variablesmultiply

 var multiply = new Function("x", "y", "return x * y;"); 

2-名为multiply的函数的函数声明:

 function multiply(x, y) { return x * y; } 

3-分配给variablesmultiply的函数expression式:

 var multiply = function (x, y) { return x * y; }; 

4-一个名为func_name的函数expression式,分配给variablesmultiply

 var multiply = function func_name(x, y) { return x * y; }; 

即使这是一个古老的问题和答案,它讨论了一个话题,直到今天仍然引发了许多开发者循环。 我无法计算我采访过的JavaScript开发人员数量,他们无法告诉我函数声明和函数expression式之间的区别, 以及谁不知道立即调用的函数expression式是什么。

不过,我想提一下,Premasagar的代码片段即使给了它一个名称标识符也不起作用,这是非常重要的。

 function someName() { alert(2 + 2); }(); 

这不起作用的原因是JavaScript引擎将其解释为一个函数声明,然后是一个完全不相关的分组运算符,它不包含expression式,分组运算符必须包含一个expression式。 根据JavaScript,上面的代码片段相当于下面的代码片段。

 function someName() { alert(2 + 2); } (); 

另一件我想指出的事情可能对某些人来说有一些用处,那就是除了在函数定义本身之外,你为函数expression式提供的任何名称标识符在代码上下文中都是无用的。

 var a = function b() { // do something }; a(); // works b(); // doesn't work var c = function d() { window.setTimeout(d, 1000); // works }; 

当然,在函数定义中使用名称标识符对于debugging代码来说总是有帮助的,但是这完全是另一回事::-)

好的答案已经发布。 但是我想要注意的是函数声明返回一个空的完成logging:

14.1.20 – 运行时语义:评估

FunctionDeclarationfunction BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. 返回NormalCompletion (空)。

这个事实并不容易观察,因为试图获取返回值的大多数方法都会将函数声明转换为函数expression式。 但是, eval显示它:

 var r = eval("function f(){}"); console.log(r); // undefined 

我还有一点小意见。 你的代码将会做一些小改动:

 var x = function(){ alert(2 + 2); }(); 

我使用上面的语法,而不是更广泛的传播版本:

 var module = (function(){ alert(2 + 2); })(); 

因为我没有设法让缩进在vim中为JavaScript文件正常工作。 vim似乎不喜欢左括号内的大括号。

在JavaScript中,这被称为立即调用函数expression式(IIFE) 。

为了使其成为函数expression式,您必须:

  1. 用()括起来

  2. 在它之前放置一个void操作符

  3. 将其分配给一个variables。

否则,它将被视为函数定义,然后您将无法通过以下方式同时调用/调用它:

  function (arg1) { console.log(arg1) }(); 

以上将给你错误。 因为你只能立即调用一个函数expression式。

这可以通过几种方式实现:方式1:

 (function(arg1, arg2){ //some code })(var1, var2); 

方式2:

 (function(arg1, arg2){ //some code }(var1, var2)); 

方式3:

 void function(arg1, arg2){ //some code }(var1, var2); 

方式4:

  var ll = function (arg1, arg2) { console.log(arg1, arg2); }(var1, var2); 

以上所有将立即调用函数expression式。

 (function(){ alert(2 + 2); })(); 

上面是有效的语法,因为在圆括号内传递的任何东西都被认为是函数expression式

 function(){ alert(2 + 2); }(); 

以上是无效的语法。 由于Java脚本语法分析器在函数关键字后面查找函数名称,因为它找不到任何内容,所以会引发错误。

它们可以和参数参数一起使用

 var x = 3; var y = 4; (function(a,b){alert(a + b)})(x,y) 

会导致7

也许最简单的答案就是这个

 function() { alert( 2 + 2 ); } 

定义 (匿名)函数的函数文字 。 额外的() – 对,被解释为一个expression式,不是在顶层,只有文字。

 (function() { alert( 2 + 2 ); })(); 

是在一个expression式语句调用一个匿名函数。