为什么你需要在同一行上调用一个匿名函数?

我正在阅读关于closures的一些post,并在任何地方看到这个post,但没有明确的解释它是如何工作的 – 每次我只是被告知使用它…:

// Create a new anonymous function, to use as a wrapper (function(){ // The variable that would, normally, be global var msg = "Thanks for visiting!"; // Binding a new function to a global object window.onunload = function(){ // Which uses the 'hidden' variable alert( msg ); }; // Close off the anonymous function and execute it })(); 

好吧,我看到我们将创build新的匿名函数,然后执行它。 所以之后,这个简单的代码应该工作(和它):

 (function (msg){alert(msg)})('SO'); 

我的问题是这里发生了什么样的魔法? 我以为,当我写道:

 (function (msg){alert(msg)}) 

那么就会创build一个新的未命名的函数,如函数“”(msg)…

但那为什么不工作?

 (function (msg){alert(msg)}); ('SO'); 

为什么需要在同一行?

你能指点我一些post还是给我一个解释?

在函数定义之后放下分号。

 (function (msg){alert(msg)}) ('SO'); 

以上应该工作。

演示页面: https : //jsfiddle.net/e7ooeq6m/

我在这篇文章中讨论过这种模式:

jQuery和$问题

编辑:

如果你看ECMA脚本规范 ,有三种方法可以定义一个函数。 (页98,部分13function定义)

1.使用Function构造函数

 var sum = new Function('a','b', 'return a + b;'); alert(sum(10, 20)); //alerts 30 

2.使用函数声明。

 function sum(a, b) { return a + b; } alert(sum(10, 10)); //Alerts 20; 

3.functionexpression

 var sum = function(a, b) { return a + b; } alert(sum(5, 5)); // alerts 10 

所以你可能会问,声明和expression有什么区别?

从ECMA脚本规范:

FunctionDeclaration:函数标识符(FormalParameterListopt){FunctionBody}

FunctionExpression:function Identifieropt(FormalParameterListopt){FunctionBody}

如果您注意到,“标识符”对于函数expression式是可选的。 而当你不给一个标识符,你创build一个匿名函数。 这并不意味着你不能指定一个标识符。

这意味着以下是有效的。

 var sum = function mySum(a, b) { return a + b; } 

重要的一点是,只能在mySum函数体内使用'mySum',而不能在外部使用。 看下面的例子:

 var test1 = function test2() { alert(typeof test2); } alert(typeof(test2)); //alerts 'undefined', surprise! test1(); //alerts 'function' because test2 is a function. 

现场演示

比较这个

  function test1() { alert(typeof test1) }; alert(typeof test1); //alerts 'function' test1(); //alerts 'function' 

有了这些知识,我们试着分析一下你的代码。

当你有类似的代码时,

  function(msg) { alert(msg); } 

你创build了一个函数expression式。 你可以通过将它包装在括号中来执行这个函数expression式。

  (function(msg) { alert(msg); })('SO'); //alerts SO. 

这就是所谓的自我调用function。

当你调用(function(){})时你正在做的是返回一个函数对象。 当你追加()到它时,它被调用,并且正文中的任何东西都被执行。 The ; 表示语句的结尾,这就是第二次调用失败的原因。

有一件事我觉得困惑的是“()”是分组操作符。

这是你的基本声明函数。

防爆。 1:

 var message = 'SO'; function foo(msg) { alert(msg); } foo(message); 

函数是对象,可以分组。 所以让我们抛出parens的function。

防爆。 2:

 var message = 'SO'; function foo(msg) { //declares foo alert(msg); } (foo)(message); // calls foo 

现在,我们可以使用基本replace来声明它,而不是声明和立即调用相同的函数。

防爆。 3。

 var message = 'SO'; (function foo(msg) { alert(msg); })(message); // declares & calls foo 

最后,我们不需要额外的foo,因为我们不使用名称来调用它! 函数可以是匿名的。

防爆。 4。

 var message = 'SO'; (function (msg) { // remove unnecessary reference to foo alert(msg); })(message); 

要回答你的问题,请回头参考例2.你的第一行声明了一些无名函数并对它进行了分组,但是没有调用它。 第二行分组一个string。 两者都无所作为。 (文森特的第一个例子)

 (function (msg){alert(msg)}); ('SO'); // nothing. (foo); (msg); //Still nothing. 

 (foo) (msg); //works 

匿名函数不是名称为“”的函数。 这只是一个没有名字的function。

像JavaScript中的任何其他值一样,函数不需要创build名称。 尽pipe将其与任何其他值绑定到一个名称是更有用的。

但是,像其他任何价值一样,你有时想使用它而不把它绑定到一个名字上。 这是自我调动的模式。

这里是一个函数和一个数字,没有绑定,他们什么都不做,也不能使用:

 function(){ alert("plop"); } 2; 

所以我们必须将它们存储在一个variables中才能使用它们,就像其他值一样:

 var f = function(){ alert("plop"); } var n = 2; 

你也可以使用syntally sugar将函数绑定到一个variables上:

 function f(){ alert("plop"); } var n = 2; 

但是,如果命名它们不是必需的,会导致更多的混淆和更less的可读性,您可以立即使用它们。

 (function(){ alert("plop"); })(); // will display "plop" alert(2 + 3); // will display 5 

在这里,我的函数和我的数字没有绑定到一个variables,但他们仍然可以使用。

像这样说,它看起来像自我调用function没有实际价值。 但是您必须记住JavaScript范围分隔符是函数而不是块({})。

所以一个自调用函数实际上和C ++,C#或Java块有相同的含义。 这意味着内部创build的variables不会在范围之外“泄漏”。 这在JavaScript中非常有用,以免污染全球范围。

这正是JavaScript的工作原理。 你可以声明一个命名的函数:

 function foo(msg){ alert(msg); } 

并称之为:

 foo("Hi!"); 

或者,你可以声明一个匿名函数:

 var foo = function (msg) { alert(msg); } 

并呼吁:

 foo("Hi!"); 

或者,您可以永远不要将该函数绑定到名称上:

 (function(msg){ alert(msg); })("Hi!"); 

函数也可以返回函数:

 function make_foo() { return function(msg){ alert(msg) }; } (make_foo())("Hi!"); 

make_foo定义的“var”variables将被make_foo返回的每个函数closures,这是值得的。 这是一个闭包,这意味着一个函数对值的任何更改都会被另一个函数看到。

这可以让你封装信息,如果你愿意的话:

 function make_greeter(msg){ return function() { alert(msg) }; } var hello = make_greeter("Hello!"); hello(); 

这几乎是所有的编程语言,但Java的作品。

你显示的代码,

 (function (msg){alert(msg)}); ('SO'); 

两个陈述组成。 第一个是产生一个函数对象的expression式(它将被垃圾回收,因为它没有被保存)。 第二个是产生一个string的expression式。 要将该函数应用于string,您需要在创build函数时将该string作为parameter passing给函数(上面还显示了这个函数),或者您需要将该函数实际存储在variables中,以便可以在以后的时间,在你的闲暇时间使用它。 像这样:

 var f = (function (msg){alert(msg)}); f('SO'); 

请注意,通过在一个variables中存储一个匿名函数(一个lambda函数),你的名字是有效的。 因此,您可以定义一个常规函数:

 function f(msg) {alert(msg)}; f('SO'); 

总结前面的评论:

 function() { alert("hello"); }(); 

未分配给variables时,会产生语法错误。 该代码被parsing为一个函数语句(或定义),从而使得括号内的括号在语法上不正确。 在函数部分周围添加括号告诉解释器(和程序员),这是一个函数expression式(或调用),如

 (function() { alert("hello"); })(); 

这是一个自我调用函数,意味着它是匿名创build的,因为调用发生在声明的同一行,所以立即运行。 这个自我调用函数用熟悉的语法来表示,以调用无参数函数,并在函数的名字周围加上括号: (myFunction)();

有一个很好的讨论JavaScript函数的语法 。

这个答案与问题没有严格的关系,但是你可能会感兴趣的是,这种语法特征并不是特定于函数的。 例如,我们总是可以这样做:

 alert( {foo: "I am foo", bar: "I am bar"}.foo ); // alerts "I am foo" 

与function有关。 因为它们是从Function.prototypeinheritance的对象,所以我们可以这样做:

 Function.prototype.foo = function () { return function () { alert("foo"); }; }; var bar = (function () {}).foo(); bar(); // alerts foo 

而且你知道,我们甚至不必为了执行而用括号括起函数。 无论如何,只要我们试图将结果分配给一个variables。

 var x = function () {} (); // this function is executed but does nothing function () {} (); // syntax error 

另一件你可以用函数做的事情,就是声明它们,就是调用它们上面的new运算符,并获得一个对象。 以下是等同的:

 var obj = new function () { this.foo = "bar"; }; var obj = { foo : "bar" }; 

JavaScript函数还有一个属性。 如果你想recursion调用同一个匿名函数。

 (function forInternalOnly(){ //you can use forInternalOnly to call this anonymous function /// forInternalOnly can be used inside function only, like var result = forInternalOnly(); })(); //this will not work forInternalOnly();// no such a method exist 

没有括号的例子:

 void function (msg) { alert(msg); } ('SO'); 

(这是唯一真正的使用void,afaik)

要么

 var a = function (msg) { alert(msg); } ('SO'); 

要么

 !function (msg) { alert(msg); } ('SO'); 

工作也是如此。 void导致expression评估,以及任务和爆炸。 最后一个适用于~+-deletetypeof ,一些一元运算符( void也是一个)。 不工作是因为++ ,因为需要一个variables。

换行符是没有必要的。

我对提问者的问题的理解是:

这个魔法是如何工作的:

 (function(){}) ('input') // Used in his example 

我可能是错的。 然而,人们熟悉的惯例是:

 (function(){}('input') ) 

原因是JavaScript括号AKA ()不能包含语句,当分析器遇到函数关键字时,它知道把它parsing为函数expression式而不是函数声明。

来源:博客文章立即调用函数expression式(IIFE)

这是一个自我执行的匿名函数。 第一组括号包含要执行的expression式,第二组括号执行这些expression式。

 (function () { return ( 10 + 20 ); })(); 

Peter Michaux讨论了一对重要的括号中的差异。

当试图隐藏来自父命名空间的variables时,这是一个有用的构造。 函数中的所有代码都包含在函数的私有范围内,这意味着函数的外部根本无法访问,从而使其成为私有的。

看到:

  1. closures(计算机科学)
  2. JavaScript命名空间
  3. 重要的一对Javascript括号

它不工作的简单理由不是因为; 指示匿名函数的结束。 这是因为没有()函数调用的结尾,它不是一个函数调用。 那是,

 function help() {return true;} 

如果你调用result = help(); 这是一个函数的调用,将返回true。

如果你打电话给result = help; 这不是一个电话。 这是一个分配,帮助处理就像分配给结果的数据一样。

你所做的是通过添加分号来声明/实例化一个匿名函数,

 (function (msg) { /* Code here */ }); 

然后试着用括号括起来在另一个语句中调用它…显然,因为函数没有名字,但是这不起作用:

 ('SO'); 

口译员把第二行的括号看作是一个新的指令/陈述,因此它不起作用,即使你这样做了:

 (function (msg){/*code here*/});('SO'); 

它仍然不起作用,但是当你删除分号时它会起作用,因为解释器会忽略空格和小车,并将完整的代码视为一个语句。

 (function (msg){/*code here*/}) // This space is ignored by the interpreter ('SO'); 

结论:函数调用不是一个没有()的函数调用,除非在特定条件下(例如被另一个函数调用),即,即使没有包括括号,onload ='help'也会执行帮助函数。 我相信setTimeout和setInterval也允许这种types的函数调用,而且我也相信解释器无论如何都会在后面添加括号,这使得我们回到“函数调用不是没有括号的函数调用”。

 (function (msg){alert(msg)}) ('SO'); 

这是使用匿名函数作为许多JavaScript框架使用的闭包的常用方法。

编译代码时,调用的这个函数是自动的。

如果放置; 在第一行,编译器将其视为两条不同的行。 所以你不能得到和上面一样的结果。

这也可以写成:

 (function (msg){alert(msg)}('SO')); 

有关更多详细信息,请参阅JavaScript /匿名函数

  1. 匿名函数是在运行时dynamic声明的函数。 它们被称为匿名函数,因为它们没有像正常函数一样被赋予一个名字。

    匿名函数是使用函数运算符而不是函数声明来声明的。 您可以使用函数运算符在需要放置expression式的任何地方创build新函数。 例如,你可以声明一个新的函数作为函数调用的参数,或者分配另一个对象的属性。

    以下是一个命名函数的典型示例:

    函数flyToTheMoon(){alert(“Zoom!Zoom!Zoom!”); } flyToTheMoon(); 以下是与匿名函数相同的示例:

    var flyToTheMoon = function(){alert(“Zoom!Zoom!Zoom!”); } flyToTheMoon();

    详情请阅读:

    http://helephant.com/2008/08/23/javascript-anonymous-functions/

IIFE只是简单地划分函数,隐藏msgvariables,以免“污染”全局名称空间。 事实上,只要保持简单,就像下面这样做,除非你正在build立一个十亿美元的网站。

 var msg = "later dude"; window.onunload = function(msg){ alert( msg ); }; 

你可以使用Revealing Module Pattern命名空间你的msg属性,如:

 var myScript = (function() { var pub = {}; //myscript.msg pub.msg = "later dude"; window.onunload = function(msg) { alert(msg); }; //API return pub; }()); 

匿名函数意味着一次性交易,您可以即时定义一个函数,从而根据您提供的input生成一个输出。 除了你没有提供input。 相反,你在第二行写了一些东西('SO'); – 与function无关的独立声明。 你期待什么? 🙂

另一个angular度来看

首先,你可以声明一个匿名函数:

 var foo = function(msg){ alert(msg); } 

那你叫它:

 foo ('Few'); 

因为foo = function(msg){alert(msg);}所以你可以把fooreplace成:

 function(msg){ alert(msg); } ('Few'); 

但是你应该将整个匿名函数包装在一对大括号中,以避免parsing时声明函数的语法错误。 那我们呢,

 (function(msg){ alert(msg); }) ('Few'); 

通过这种方式,我很容易理解。

当你做了:

 (function (msg){alert(msg)}); ('SO'); 

由于分号,您在('SO')之前结束了函数。 如果你只写:

 (function (msg){alert(msg)}) ('SO'); 

它会工作。

工作示例: http : //jsfiddle.net/oliverni/dbVjg/