解释封装的匿名函数语法

感情迁移 提交于 2020-02-26 07:03:18

摘要

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


我知道的

在JavaScript中,将创建一个命名函数,如下所示:

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

您还可以创建一个匿名函数并将其分配给变量:

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

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

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

在创建模块化脚本时,这很有用,以避免因潜在冲突的变量而使当前范围或全局范围混乱(例如Greasemonkey脚本,jQuery插件等)。

现在,我明白了为什么这样做了。 方括号将内容括起来,仅显示结果(我敢肯定有一种更好的描述方式),例如(2 + 2) === 4


我不明白的

但是我不明白为什么这不能同样有效:

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

你能跟我解释一下吗?


#1楼

即使这是一个古老的问答,它仍然讨论了一个主题,该主题至今仍使许多开发人员陷入困境。 我无法计算我采访过的JavaScript开发人员候选人的人数,这些候选人无法告诉我函数声明和函数表达式之间的区别, 并且谁也不知道立即调用的函数表达式是什么。

不过,我想提一件事,非常重要的一点是,即使Premasagar给出了名称标识符,它的代码段也无法使用。

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

之所以不起作用,是因为JavaScript引擎将其解释为函数声明,然后是一个完全不相关的不包含表达式的分组运算符,并且分组运算符必须包含一个表达式。 根据JavaScript,以上代码片段等同于以下代码片段。

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

();

我想指出的另一件事可能是对某些人有用:您为函数表达式提供的任何名称标识符在代码的上下文中几乎没有用,除非是在函数定义本身内部。

var a = function b() {
    // do something
};
a(); // works
b(); // doesn't work

var c = function d() {
    window.setTimeout(d, 1000); // works
};

当然,在调试代码时,将名称标识符与函数定义一起使用总是很有帮助的,但这完全是另一回事... :-)


#2楼

它不起作用是因为它被解析为FunctionDeclaration ,并且函数声明的名称标识符是强制性的

当用括号将其括起来时,它将被视为FunctionExpression ,并且可以命名或不命名函数表达式。

FunctionDeclaration的语法如下所示:

function Identifier ( FormalParameterListopt ) { FunctionBody }

FunctionExpression s:

function Identifieropt ( FormalParameterListopt ) { FunctionBody }

如您所见, FunctionExpressionIdentifier (Identifier opt )标记是可选的,因此我们可以使用一个未定义名称的函数表达式:

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

命名函数表达式:

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

括号(正式称为分组运算符 )只能包围表达式,并且会对函数表达式求值。

这两个语法产生可能会模棱两可,并且看起来可能完全相同,例如:

function foo () {} // FunctionDeclaration

0,function foo () {} // FunctionExpression

解析器知道它是FunctionDeclaration还是FunctionExpression ,具体取决于它出现的上下文

在上面的示例中,第二个是表达式,因为逗号运算符也只能处理表达式。

另一方面, 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-具有不同的行为。 它们的语法包含一个非标准的 Function语句,这意味着该函数将在运行时而不是在解析时进行评估,这与FunctionDeclaration s一样。 在这些实现中,我们将定义第一个函数。


可以用不同的方式声明函数,请比较以下内容

1-一个函数,该函数使用分配给变量的Function构造函数 乘法

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

2-名为乘法的函数的函数声明:

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

3-分配给变量multipli的函数表达式:

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

4-命名函数表达式func_name ,分配给变量multipli

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

#3楼

也许简短的答案是

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

定义 (匿名)函数的函数文字 。 附加的()对,被解释为一个表达式,不希望在顶层使用,仅在文字上使用。

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

调用匿名函数的表达式语句中。


#4楼

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

上面是有效的语法,因为括号内传递的任何内容都被视为函数表达式。

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

上面的语法无效。 因为Java脚本语法分析器在function关键字之后查找功能名称,因为它找不到任何东西,因此会引发错误。


#5楼

好的答案已经发布。 但我想指出,函数声明返回一个空的完成记录:

14.1.20-运行时语义:评估

FunctionDeclarationfunction BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. 返回NormalCompletion (空)。

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

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

调用空的完成记录没有任何意义。 这就是为什么function f(){}()无法工作。 实际上,JS引擎甚至没有尝试调用它,括号被视为另一条语句的一部分。

但是,如果将函数包装在括号中,它将变成函数表达式:

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

函数表达式返回一个函数对象。 因此,您可以将其称为: (function f(){})()

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!