【问题标题】:Why do you need to invoke an anonymous function on the same line?为什么需要在同一行调用匿名函数?
【发布时间】:2010-11-11 12:34:22
【问题描述】:

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

// 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)})

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

但是为什么这不起作用呢?

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

为什么要在同一行?

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

【问题讨论】:

  • 在其他语言中,如果您想查看所涉及的低级结构,这些称为函数指针或委托。
  • 你有一个 ;在第一行
  • 既然您知道它是如何工作的......不要使用它。我们应该stop writing anonymous functions。只需再添加几个字符,我们就可以给我们的函数一个真实的名称,并使调试 Javascript 代码变得更加容易!
  • (function (msg){alert(msg)})('SO'); 行完全独立运行。它与您之前发布的其他匿名函数无关。这是两个完全独立的匿名函数。你必须立即调用一个匿名函数,因为它没有名字,以后也不能被引用。

标签: javascript anonymous-function iife


【解决方案1】:

去掉函数定义后的分号。

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

上面应该可以工作。

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

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

jQuery and $ questions

编辑:

如果您查看ECMA script specification,您可以通过 3 种方式定义函数。 (第 98 页,第 13 节函数定义)

1。使用函数构造函数

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 脚本规范:

功能声明: 函数标识符(FormalParameterListopt){ FunctionBody }

函数表达式: 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.

【讨论】:

  • 是的,但为什么呢?为什么它需要作为内联?无论我将使用多少空白。
  • 正如我所写,分号终止了匿名函数定义。因为它没有名字(它是匿名的,呃!),你将无法再调用它。如果你不放分号,那么函数仍然可以执行。
  • 我认为自动分号插入会在这种情况下放入分号,但事实并非如此。所以你是对的。
  • Nosredna,JS 在添加分号时表现得非常随意。阅读这篇详细文章:blog.boyet.com/blog/javascriptlessons/…
  • 是的,我看到了 (function (msg){alert(msg)})('SO');作品。我只是问为什么它有效。这是在哪里指定的或这是什么类型的 JS 功能。所以一旦我调用: (function (msg){alert(msg)}) 函数会发生什么?它会被 GC 处理吗?
【解决方案2】:

它被称为自调用函数。

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

【讨论】:

  • 啊,我明白了,所以这只是一些特殊的 JS 语法,对吧?最喜欢这个解释!简单而简短:)
  • 我认为说身体将被“评估”是不正确的。它就像任何其他功能一样执行。因为它是匿名的,所以您要么将引用保存在某处,要么立即执行它。
  • 就我个人而言,我什至不喜欢“自调用函数”这个词。并不是那个函数在调用自己。程序员编写了这些括号来调用它。
  • 这不是“特殊语法”,而不是其他任何东西都是特殊的。实际上,“函数名 (args) { BLOCK }”形式更加“特殊”。它实际上是不必要的糖;然而,这才是真正让事情发生的原因。
  • 不错的文章链接。它说明了为什么有人会使用这个引用:“为了保护全局对象,所有 JavaScript 应用程序都应该写在一个自调用函数中。这将创建一个应用程序范围,可以在其中创建变量而不必担心它们发生冲突与其他应用程序。”并且还注意到“一旦函数终止,变量将被丢弃,全局对象保持不变。”
【解决方案3】:

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

这是您声明的基本函数。

例如。 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

【讨论】:

  • 谢谢。你的例子很清楚。我不知道 JavaScript 中的括号会以这种方式改变代码的含义。我来自 Java 背景,所以我几乎每天使用 JavaScript 都会学到一些关于 JavaScript 的新东西(而且常常是出乎意料的)。
  • 感谢您一步一步做,这比我见过的任何其他解释都要好。 +1
  • 这里是 AHA 的重要时刻-感谢您用替换来说明。 +100
  • 我读过的关于匿名函数的最佳解释之一。非常感谢!
【解决方案4】:

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

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

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

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

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

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

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

您还可以使用语法糖将函数绑定到变量:

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

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

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

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

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

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

【讨论】:

  • 好帖子。那么 'function(){ alert("plop"); 会发生什么? }' 当我执行它的时候?它会被 GC 处理吗?
  • 函数(){ alert("plop"); } 指令只是分配函数但不执行它也不将它绑定到一个变量。由于创建的函数没有绑定任何变量,所以会很快被GC。
  • This SO thread 超出了我们在这里讨论的范围,但它解释了分离 JavaScript 命名空间的方法——并包括使用自调用函数的示例。
【解决方案5】:

这就是 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”定义的任何变量都将被make_foo 返回的每个函数关闭,这是毫无价值的。这是一个闭包,这意味着一个函数对值所做的任何更改都将对另一个函数可见。

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

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

var hello = make_greeter("Hello!");

hello();

这就是除了 Java 之外几乎所有编程语言的工作原理。

【讨论】:

    【解决方案6】:

    你展示的代码,

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

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

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

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

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

    【讨论】:

      【解决方案7】:

      对以往cmets的总结:

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

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

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

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

      a good SO discussion JavaScript function syntax

      【讨论】:

        【解决方案8】:

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

        这个魔法是如何工作的:

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

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

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

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

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

        【讨论】:

          【解决方案9】:

          不带括号的例子:

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

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

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

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

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

          不需要换行。

          【讨论】:

          • @Bergi on ie11 delete 有效。即使是'use strict';。这也有效:delete (3 + 4);
          • 糟糕,我的错误。 "2) 如果 Type(ref) 不是 Reference,则返回 true。" 它只对无法解析的实际引用抛出错误。
          【解决方案10】:

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

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

          【讨论】:

            【解决方案11】:

            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
            

            【讨论】:

            • +1 添加了一个小示例,以便更清晰 :-) 第一次阅读时,我不得不重读 4 次。
            【解决方案12】:

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

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

            Peter Michaux 讨论了An Important Pair of Parentheses 中的区别。

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

            见:

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

            【讨论】:

              【解决方案13】:

              另一种观点

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

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

              那你叫它:

              foo ('Few');
              

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

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

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

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

              这样一来,我就很容易理解了。

              【讨论】:

                【解决方案14】:

                当你这样做时:

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

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

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

                它会起作用的。

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

                【讨论】:

                  【解决方案15】:

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

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

                  【讨论】:

                  • 我不明白为什么这会受到如此多的反对。我认为这是一个可以接受的答案? ://
                  【解决方案16】:
                  (function (msg){alert(msg)})
                  ('SO');
                  

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

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

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

                  这也可以写成:

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

                  更多详情,请查看JavaScript/Anonymous Functions

                  【讨论】:

                  • 据我所知,JavaScript 不会“编译”
                  【解决方案17】:
                  1. 匿名函数是动态声明的函数 运行。它们被称为匿名函数,因为它们不是 以与普通函数相同的方式命名。

                    匿名函数使用函数运算符来声明 的函数声明。您可以使用函数运算符 在任何可以放置表达式的地方创建一个新函数。为了 例如,您可以将一个新函数声明为 函数调用或分配另一个对象的属性。

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

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

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

                    详情请看这里:

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

                  【讨论】:

                    【解决方案18】:

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

                    【讨论】:

                      【解决方案19】:

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

                      【讨论】:

                      • 不是 100% 正确的。这也是一个匿名函数,可以重复使用:var foo = function() {};。其他一切都很好。
                      猜你喜欢
                      • 2013-12-07
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2019-12-14
                      相关资源
                      最近更新 更多