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

Why do you need to invoke an anonymous function on the same line?

我读了一些关于闭包的文章,到处都能看到,但是没有明确的解释它是如何工作的-每次我被要求使用它时……

1
2
3
4
5
6
7
8
9
10
11
12
// 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
})();

好的,我看到我们将创建新的匿名函数,然后执行它。所以在这之后,这个简单的代码就可以工作了(它确实做到了):

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

我的问题是这里发生了什么样的魔法?我觉得当我写:

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

然后会创建一个新的未命名函数,就像函数"(msg)……

但是为什么这不管用呢?

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

为什么需要在同一行?

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


在函数定义后删除分号。

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

上面应该有用。

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

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

jquery和$questions

编辑:

如果您查看ECMA脚本规范,有三种方法可以定义函数。(第98页第13节功能定义)

1。使用函数构造函数

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

2。使用函数声明。

1
2
3
4
5
6
function sum(a, b)
{
    return a + b;
}

alert(sum(10, 10)); //Alerts 20;

三。函数表达式

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

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

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

来自ECMA脚本规范:

FunctionDeclaration :
function Identifier ( FormalParameterListopt ){ FunctionBody
}

FunctionExpression :
function Identifieropt ( FormalParameterListopt ){ FunctionBody
}

如果您注意到,"identifier"对于函数表达式是可选的。如果不提供标识符,则创建一个匿名函数。这并不意味着您不能指定标识符。

这意味着以下内容有效。

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

需要注意的一点是,只能在mysum函数体内部使用mysum,而不能在外部使用。请参见以下示例:

1
2
3
4
5
var test1 = function test2() { alert(typeof test2); }

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

test1(); //alerts 'function' because test2 is a function.

现场演示

将此与

1
2
3
4
5
 function test1() { alert(typeof test1) };

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

 test1(); //alerts 'function'

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

当你有代码的时候,

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

您创建了一个函数表达式。您可以通过将函数表达式包装在括号中来执行它。

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


它被称为自调用函数。

调用(function(){})时所做的是返回一个函数对象。当您将()附加到它之后,它将被调用并执行主体中的任何内容。;表示语句结束,这就是第二次调用失败的原因。


有一件事让我感到困惑:"()"是分组运算符。

这是您的基本声明函数。

第1章:

1
2
3
4
5
6
7
var message = 'SO';

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

foo(message);

函数是对象,可以分组。那么让我们围绕函数抛出parens。

第2章:

1
2
3
4
5
6
7
var message = 'SO';

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

(foo)(message);     // calls foo

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

第3章。

1
2
3
4
5
var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo

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

第4章。

1
2
3
4
5
var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);

要回答您的问题,请参阅示例2。第一行声明一些无名称函数并对其进行分组,但不调用它。第二行对字符串进行分组。两者都不做任何事。(文森特的第一个例子。)

1
2
3
4
5
(function (msg){alert(msg)});  
('SO');                       // nothing.

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

但是

1
2
(foo)
(msg); //works


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

与JavaScript中的任何其他值一样,函数不需要创建名称。尽管实际上像其他值一样将它绑定到名称上更有用。

但和其他值一样,有时您希望使用它而不将其绑定到名称。这就是自调用模式。

这里有一个函数和一个数字,不受约束,它们不做任何事情,永远不能使用:

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

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

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

还可以使用合成糖将函数绑定到变量:

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

但是,如果命名它们不是必需的,并且会导致更多的混乱和更少的可读性,那么您可以立即使用它们。

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

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

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

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


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

1
2
3
function foo(msg){
   alert(msg);
}

并称之为:

1
foo("Hi!");

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

1
2
3
var foo = function (msg) {
    alert(msg);
}

并称之为:

1
foo("Hi!");

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

1
2
3
(function(msg){
   alert(msg);
 })("Hi!");

函数还可以返回函数:

1
2
3
4
5
function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");

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

如果您需要,这允许您封装信息:

1
2
3
4
5
6
7
function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

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


你展示的代码,

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

由两个语句组成。第一个是一个表达式,它生成一个函数对象(然后由于没有保存而被垃圾收集)。第二个表达式生成一个字符串。要将函数应用于字符串,您要么需要在创建函数时将字符串作为参数传递给函数(上面也显示了这个参数),要么需要将函数实际存储在变量中,以便您以后可以在空闲时应用它。像这样:

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

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

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


总结之前的评论:

1
2
3
function() {
  alert("hello");
}();

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

1
2
3
(function() {
  alert("hello");
})();

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

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


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

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

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

1
2
3
4
5
6
7
8
9
Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

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

bar(); // alerts foo

你知道,为了执行函数,我们甚至不需要用括号括起来。无论如何,只要我们尝试将结果赋给一个变量。

1
2
3
var x = function () {} (); // this function is executed but does nothing

function () {} (); // syntax error

在声明函数后,您可以对函数做的另一件事情是对它们调用new运算符并获取对象。以下是等效的:

1
2
3
4
5
6
7
var obj = new function () {
    this.foo ="bar";
};

var obj = {
    foo :"bar"
};

javascript函数还有一个属性。如果您想递归调用相同的匿名函数。

1
2
3
4
5
6
7
8
9
(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


我对提问者问题的理解如下:

这种魔力是如何发挥作用的:

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

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

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

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

来源:blog post立即调用函数表达式(iife)


不带括号的示例:

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

(这是虚空的唯一真正用途,阿法克)

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

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

工作也一样。void导致表达式进行评估,以及赋值和bang。最后一个与~+-deletetypeof合作,一些一元运算符(void也是一个。由于变量的要求,不工作的情况类似于++--

不需要换行符。


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

1
2
3
(function () {
    return ( 10 + 20 );
})();

彼得·米肖在一对重要的括号中讨论了这种区别。

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

见:

  • 关闭(计算机科学)
  • javascript名称间距
  • 重要的一对javascript括号

  • IIFE只是简单地划分函数并隐藏msg变量,以避免"污染"全局命名空间。事实上,只要保持简单,像下面这样做,除非你正在建立一个十亿美元的网站。

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

    您可以使用如下暴露的模块模式来命名您的msg属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var myScript = (function() {
        var pub = {};
        //myscript.msg
        pub.msg ="later dude";
        window.onunload = function(msg) {
            alert(msg);
        };
        //API
        return pub;
    }());

    另一种观点

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

    1
    2
    3
    var foo = function(msg){
     alert(msg);
    }

    然后你叫它:

    1
    foo ('Few');

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

    1
    2
    3
    function(msg){
     alert(msg);
    } ('Few');

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

    1
    2
    3
    (function(msg){
     alert(msg);
    }) ('Few');

    这样对我来说很容易理解。


    它不能工作的简单原因不是因为;指示匿名函数的结束。这是因为在函数调用的末尾没有(),它不是函数调用。也就是说,

    1
    function help() {return true;}

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

    如果你打电话给result = help;,这不是电话。这是一种分配,帮助被视为要分配给结果的数据。

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

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

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

    1
    ('SO');

    解释器将第二行的括号视为新的指令/语句,因此它不起作用,即使您这样做:

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

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

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

    结论:一个函数调用不是一个没有()的函数调用,除非在特定的条件下,如被另一个函数调用,即onload='help'将执行帮助函数,即使没有包含括号。我相信setTimeout和setInterval也允许这种类型的函数调用,而且我也相信解释器在后台添加了括号,不管怎样,这让我们回到"函数调用不是没有括号的函数调用"。


    当你这样做的时候:

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

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

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

    它会起作用的。

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


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

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

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

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

    这也可以写成:

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

    有关更多详细信息,请查看javascript/匿名函数。


  • 匿名函数是动态声明在运行时。它们被称为匿名函数,因为它们不是以与普通函数相同的方式给出名称。

    匿名函数是使用函数运算符声明的。函数声明的。您可以使用函数运算符在表达式有效的地方创建一个新函数。为了示例可以将新函数声明为函数调用或分配另一对象的属性。

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

    函数flyToTheMoon()alert("缩放!变焦!变焦!";}flytoThemoon();这里有一个匿名的例子功能:

    var flytoThemoon=function()alert("缩放!变焦!变焦!";}FrtotoMooon();

    有关详细信息,请阅读以下内容:

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


  • 匿名函数应该是一次性的,您可以动态定义一个函数,以便它从您提供的输入中生成输出。但您没有提供输入。相反,您在第二行写了一些东西("so");—一个与函数无关的独立语句。你期待什么?:)