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

时间:2009-07-16 20:26:26

标签: 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
})();

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

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

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

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

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

但是为什么这不起作用?

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

为什么需要在同一行?

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

19 个答案:

答案 0 :(得分:374)

在函数定义后删除分号。

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

上面应该有用。

DEMO Page:https://jsfiddle.net/e7ooeq6m/

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

jQuery and $ questions

修改

如果查看ECMA script specification,可以使用3种方法定义函数。 (第98页,第13节功能定义)

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。功能表达

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

alert(sum(5, 5)); // alerts 10

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

来自ECMA Script规范:

  

功能声明:       function Identifier(FormalParameterListopt){FunctionBody   }

     

FunctionExpression:       function Identifieropt(FormalParameterListopt){FunctionBody   }

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

这意味着以下内容有效。

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.

Live Demo

将此与

进行比较
 function test1() { alert(typeof test1) };

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

 test1(); //alerts 'function'

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

如果有代码,

    function(msg) { alert(msg); }

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

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

答案 1 :(得分:126)

它被称为自调用函数。

当您致电(function(){})时,您正在做的是返回一个功能对象。当您向其追加()时,会调用它并执行正文中的任何内容。 ;表示语句的结束,这就是第二次调用失败的原因。

答案 2 :(得分:93)

我发现令人困惑的一件事是“()”是分组运算符。

这是您的基本声明功能。

实施例。 1:

var message = 'SO';

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

foo(message);

功能是对象,可以分组。所以让我们围绕这个功能抛出麻烦。

实施例。 2:

var message = 'SO';

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

(foo)(message);     // calls foo

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

实施例。 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.您的第一行声明了一些无名函数并对其进行分组,但不会调用它。第二行将字符串分组。两者都无所作为。 (文森特的第一个例子。)

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

(foo); 
(msg); //Still nothing.

但是

(foo)
(msg); //works

答案 3 :(得分:23)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

答案 4 :(得分:19)

这就是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返回的每个函数关闭make_foo正文中用“var”定义的任何变量都是值得的。这是一个闭包,它意味着一个函数对值所做的任何更改都将被另一个函数看到。

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

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

var hello = make_greeter("Hello!");

hello();

几乎每种编程语言都可以使用Java。

答案 5 :(得分:8)

您展示的代码,

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

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

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

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

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

答案 6 :(得分:7)

以前的评论摘要:

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

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

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

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

a good SO discussion JavaScript function syntax

答案 7 :(得分:3)

这个答案并不是与问题严格相关,但您可能有兴趣发现这种语法特征并不特定于函数。例如,我们总是可以这样做:

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

与功能相关。因为它们是继承自Function.prototype的对象,所以我们可以执行以下操作:

Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

var bar = (function () {}).foo();

bar(); // alerts foo

你知道,我们甚至不必用括号括起函数来执行它们。无论如何,只要我们尝试将结果分配给变量。

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"
};

答案 8 :(得分:3)

JavaScript函数还有一个属性。如果你想以递归方式调用同一个匿名函数。

(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

答案 9 :(得分:3)

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

这个神奇的工作原理如何:

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

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

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

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

来源:博客文章 Immediately-Invoked Function Expression (IIFE)

答案 10 :(得分:3)

没有括号的例子:

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

(这是虚空的唯一真实用途,afaik)

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

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

也可以。 void导致表达式进行评估,以及赋值和爆炸。最后一个适用于~+-deletetypeof,一些一元运算符(void也是一个)。由于需要变量,因此无法工作++--

没有必要换行。

答案 11 :(得分:1)

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

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

Peter Michaux在An Important Pair of Parentheses中讨论了差异。

尝试隐藏父命名空间中的变量时,它是一个有用的构造。函数中的所有代码都包含在函数的私有范围内,这意味着它无法从函数外部访问,从而使其真正成为私有。

请参阅:

  1. Closure (computer science)
  2. JavaScript Namespacing
  3. Important Pair of Javascript Parentheses

答案 12 :(得分:0)

IIFE简单地划分了函数并隐藏了msg变量,以免污染"污染"全局命名空间。实际上,除非你正在建立一个十亿美元的网站,否则只需保持简单并且如下所示。

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;
}());

答案 13 :(得分:0)

  1. 匿名函数是动态声明的函数 运行。它们被称为匿名函数,因为它们不是 给出一个与普通函数相同的名称。

    使用函数运算符声明匿名函数 函数声明。您可以使用函数运算符 创建一个新函数,只要它放置表达式是有效的。对于 例如,您可以将新函数声明为a的参数 函数调用或指定另一个对象的属性。

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

    function flyToTheMoon(){alert(" Zoom!Zoom!Zoom!"); } flyToTheMoon();这是与匿名创建的相同示例 功能:

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

    详情请阅读:

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

答案 14 :(得分:0)

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

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

编译代码时会自动调用此函数。

如果将;放在第一行,编译器会将其视为两个不同的行。所以你不能得到与上面相同的结果。

这也可以写成:

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

有关详细信息,请查看 JavaScript/Anonymous Functions

答案 15 :(得分:0)

当你这样做时:

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

由于分号,您在('SO')之前结束了该功能。如果你只是写:

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

它会起作用。

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

答案 16 :(得分:0)

它不起作用的简单原因不是因为;表示匿名函数的结束。这是因为在函数调用结束时没有(),它不是函数调用。也就是说,

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也允许这种类型的函数调用,我也相信解释器无论如何都会在幕后添加括号,这使我们回到“函数调用不是没有括号的函数调用”。

答案 17 :(得分:0)

另一种观点

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

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

然后你称之为:

foo ('Few');

因为 foo = function(msg){alert(msg);} 所以您可以将 foo 替换为:

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

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

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

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

答案 18 :(得分:-1)

匿名函数是一次性交易,您可以动态定义函数,以便从您提供的输入中生成输出。除了你没有提供输入。相反,你在第二行写了一些东西('SO'); - 与该功能无关的独立声明。你有什么期望? :)