解释封装的匿名函数语法
概要
你能解释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 }
正如你所看到的, FunctionExpression
的Identifier
(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 – 运行时语义:评估
FunctionDeclaration :
function
BindingIdentifier(
FormalParameters)
{
FunctionBody}
- 返回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式,您必须:
-
用()括起来
-
在它之前放置一个void操作符
-
将其分配给一个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式语句中调用一个匿名函数。