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

pal*_*lig 372 javascript anonymous-function iife

我正在阅读一些关于闭包的帖子,到处都看到了这个,但是没有明确的解释它是如何工作的 - 每次我被告知要使用它......:

// 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
})();
Run Code Online (Sandbox Code Playgroud)

好的,我看到我们将创建新的匿名函数,然后执行它.所以在那之后这个简单的代码应该工作(并且确实如此):

(function (msg){alert(msg)})('SO');
Run Code Online (Sandbox Code Playgroud)

我的问题是这里发生了什么样的魔术?当我写作时我想:

(function (msg){alert(msg)})
Run Code Online (Sandbox Code Playgroud)

然后会创建一个新的未命名函数,如函数""(msg)...

但那么为什么这不起作用?

(function (msg){alert(msg)});
('SO');
Run Code Online (Sandbox Code Playgroud)

为什么它需要在同一行?

你能指点一些帖子或给我解释一下吗?

Sol*_*ogi 378

在函数定义后删除分号.

(function (msg){alert(msg)})
('SO');
Run Code Online (Sandbox Code Playgroud)

以上应该工作.

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

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

jQuery和$问题

编辑:

如果查看ECMA脚本规范,可以使用3种方法定义函数.(第98页,第13节功能定义)

1.使用Function构造函数

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

2.使用函数声明.

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

alert(sum(10, 10)); //Alerts 20;
Run Code Online (Sandbox Code Playgroud)

3.功能表达

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

alert(sum(5, 5)); // alerts 10
Run Code Online (Sandbox Code Playgroud)

所以你可能会问,声明和表达之间有什么区别?

从ECMA脚本规范:

FunctionDeclaration:function Identifier(FormalParameterListopt){FunctionBody}

FunctionExpression:function Identifieropt(FormalParameterListopt){FunctionBody}

如果您注意到,'identifier' 对于函数表达式是可选的.当您不提供标识符时,您将创建一个匿名函数.这并不意味着您无法指定标识符.

这意味着以下有效.

var sum = function mySum(a, b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)

需要注意的重要一点是,您只能在mySum函数体内使用"mySum",而不能在外部使用.请参阅以下示例:

var test1 = function test2() { alert(typeof test2); }

alert(typeof(test2)); //alerts 'undefined', surprise! 

test1(); //alerts 'function' because test2 is a function.
Run Code Online (Sandbox Code Playgroud)

现场演示

比较这个

 function test1() { alert(typeof test1) };

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

 test1(); //alerts 'function'
Run Code Online (Sandbox Code Playgroud)

有了这些知识,让我们试着分析你的代码.

当你有代码时,

    function(msg) { alert(msg); }
Run Code Online (Sandbox Code Playgroud)

您创建了一个函数表达式.您可以通过将其包含在括号内来执行此函数表达式.

    (function(msg) { alert(msg); })('SO'); //alerts SO.
Run Code Online (Sandbox Code Playgroud)

  • 正如我所写,分号终止了匿名函数定义.因为它没有名字(它是匿名的呃!),你将无法再调用它.如果你没有输入分号,那么仍然可以执行功能. (9认同)

set*_*eth 128

它被称为自调用函数.

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

  • 就个人而言,我甚至不喜欢"自我调用功能"一词.这不是函数调用自身.程序员编写了这些括号来调用它. (14认同)
  • 很好的文章链接.它指出为什么有人会使用这个引用:"为了保护全局对象,所有JavaScript应用程序都应该在自调用函数中编写.这将创建一个应用程序范围,在该范围内可以创建变量而不必担心它们发生冲突与其他应用程序." 并且还注意到"一旦函数终止,变量就会被丢弃,全局对象保持不变." (2认同)

Ben*_*min 93

我发现令人困惑的一件事是"()"是分组运算符.

这是您的基本声明功能.

防爆.1:

var message = 'SO';

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

foo(message);
Run Code Online (Sandbox Code Playgroud)

函数是对象,可以进行分组.所以让我们围绕这个功能抛出麻烦.

防爆.2:

var message = 'SO';

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

(foo)(message);     // calls foo
Run Code Online (Sandbox Code Playgroud)

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

防爆.3.

var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo
Run Code Online (Sandbox Code Playgroud)

最后,我们不需要额外的foo,因为我们没有使用名称来调用它!功能可以是匿名的.

防爆.4.

var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);
Run Code Online (Sandbox Code Playgroud)

要回答您的问题,请参阅示例2.您的第一行声明了一些无名函数并对其进行分组,但不会调用它.第二行将字符串分组.两者都无所作为.(文森特的第一个例子.)

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

(foo); 
(msg); //Still nothing.
Run Code Online (Sandbox Code Playgroud)

(foo)
(msg); //works
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.你的例子很清楚.我不知道JavaScript中的括号可以用这种方式改变代码的含义.我来自Java背景,所以我几乎每天都使用它来学习一些关于JavaScript的新东西(通常是意外的). (6认同)
  • 感谢您一步一步地做到这一点,这比我见过的任何其他解释要好得多.+1 (5认同)
  • 主要的AHA时刻 - 并感谢您用替换来说明.+100 (2认同)

Vin*_*ert 23

匿名函数不是名为""的函数.它只是一个没有名字的功能.

与JavaScript中的任何其他值一样,函数不需要创建名称.虽然将其实际绑定到名称就像其他任何值一样有用得多.

但是,与任何其他值一样,您有时希望在不将其绑定到名称的情况下使用它.这是自我调用的模式.

这是一个函数和一个数字,没有绑定,它们什么也不做,永远不能使用:

function(){ alert("plop"); }
2;
Run Code Online (Sandbox Code Playgroud)

所以我们必须将它们存储在变量中以便能够使用它们,就像任何其他值一样:

var f = function(){ alert("plop"); }
var n = 2;
Run Code Online (Sandbox Code Playgroud)

您还可以使用syntatic sugar将函数绑定到变量:

function f(){ alert("plop"); }
var n = 2;
Run Code Online (Sandbox Code Playgroud)

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

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5
Run Code Online (Sandbox Code Playgroud)

在这里,我的函数和我的数字没有绑定到变量,但它们仍然可以使用.

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

因此,自调用函数实际上具有与C++,C#或Java块相同的含义.这意味着内部创建的变量不会在范围之外"泄漏".这在JavaScript中非常有用,以免污染全局范围.

  • +1表示范围问题.令人讨厌,讨厌的问题. (4认同)
  • 函数(){alert("plop"); }指令只是分配函数但不执行它也不将它绑定到变量.由于创建的函数未绑定到任何变量,因此将快速GCed. (2认同)

jro*_*way 19

这就是JavaScript的工作原理.您可以声明一个命名函数:

function foo(msg){
   alert(msg);
}
Run Code Online (Sandbox Code Playgroud)

并称之为:

foo("Hi!");
Run Code Online (Sandbox Code Playgroud)

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

var foo = function (msg) {
    alert(msg);
}
Run Code Online (Sandbox Code Playgroud)

并称之为:

foo("Hi!");
Run Code Online (Sandbox Code Playgroud)

或者,您永远不能将函数绑定到名称:

(function(msg){
   alert(msg);
 })("Hi!");
Run Code Online (Sandbox Code Playgroud)

函数也可以返回函数:

function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");
Run Code Online (Sandbox Code Playgroud)

make_foo函数体中使用"var"定义的任何变量都将被返回的每个函数关闭,这一点毫无价值make_foo.这是一个闭包,它意味着一个函数对值的任何更改都将被另一个函数看到.

如果您愿意,这可以让您封装信息:

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();
Run Code Online (Sandbox Code Playgroud)

这几乎是几乎所有编程语言,但Java的工作原理.


Ste*_*202 8

你展示的代码,

(function (msg){alert(msg)});
('SO');
Run Code Online (Sandbox Code Playgroud)

两个陈述组成.第一个是一个表达式,它产生一个函数对象(然后它将被垃圾收集,因为它没有被保存).第二个是产生字符串的表达式.要将函数应用于字符串,您需要在创建函数时将字符串作为参数传递给函数(您也在上面显示),或者您需要将函数实际存储在变量中,以便您可以在您的闲暇时间稍后申请.像这样:

var f = (function (msg){alert(msg)});
f('SO');
Run Code Online (Sandbox Code Playgroud)

请注意,通过在变量中存储匿名函数(lambda函数),您可以有效地为其命名.因此,你也可以定义一个常规函数:

function f(msg) {alert(msg)};
f('SO');
Run Code Online (Sandbox Code Playgroud)


hot*_*309 7

总结以前的评论:

function() {
  alert("hello");
}();
Run Code Online (Sandbox Code Playgroud)

未分配给变量时,会产生语法错误.代码被解析为函数语句(或定义),这使得右括号在语法上不正确.在函数部分周围添加括号告诉解释器(和程序员)这是一个函数表达式(或调用),如

(function() {
  alert("hello");
})();
Run Code Online (Sandbox Code Playgroud)

这是一个自调用函数,这意味着它是匿名创建的并且立即运行,因为调用发生在声明它的同一行.这个自调用函数用熟悉的语法表示,用于调用无参数函数,并在函数名称周围添加括号:(myFunction)();.

一个很好的SO讨论JavaScript函数语法.


lay*_*cat 5

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

这个魔法是如何运作的:

(function(){}) ('input')   // Used in his example
Run Code Online (Sandbox Code Playgroud)

我可能错了。然而,人们熟悉的通常做法是:

(function(){}('input') )
Run Code Online (Sandbox Code Playgroud)

原因是 JavaScript 括号 AKA()不能包含语句,并且当解析器遇到 function 关键字时,它知道将其解析为函数表达式而不是函数声明。

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