【问题标题】:Difference between these two types of self executing function on JavaScriptJavaScript 上这两种自执行函数的区别
【发布时间】:2011-12-04 18:57:12
【问题描述】:

为了避免在 JavaScript 中将我的代码暴露在全局范围内,我总是使用以下自执行函数:

(function() { 
    //Code comes here
})();

我相信这也被称为自执行匿名函数。有时,我还看到以下代码用于相同目的:

(function(d){
    //Code comes here
})(document.documentElement);

我不确定这里有什么不同,所以我问这个问题。

这两种 JavaScript 上的自执行函数有什么区别(或有什么区别)?

【问题讨论】:

  • 第一个函数不带任何参数,而第二个函数有。
  • 好的,我想,但是document.documentElement 也可以在函数内部使用。为什么会这样使用?
  • Javascript 中的函数永远不会“自动执行”,无论它们是否匿名。如果它们执行的话,总会有一个对函数的调用。您的电话就在分号之前。
  • @GregHewgill 嗯,所以我用错了这个词?我总是听到这个词,但我猜在这种情况下它被用作隐喻。此代码是否与我上面引用的第一个代码相同:(function(w){})(window);
  • 这个术语是错误的,但现在大多数 JS 程序员都普遍理解为参考上面的示例。有很多人提倡使用 Immediately Invoked Function Expression(IIFE,发音为“iffy”)这个术语,我认为它更有意义。

标签: javascript


【解决方案1】:

下面的代码演示了正在发生的事情。实际上,foobar 变量不存在,函数是匿名的。

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

var bar = function(d){}
bar(document.documentElement);

(function(d){})(d) 方法称为闭包。它用于传递可变值,例如在循环中。

看一个实际的例子:

for(var i=0; i<10; i++) {
    document.links[i].onclick = function(){
        alert(i); //Will always alert 9
    }
}

实现闭包后:

for(var i=0; i<10; i++) {
    (function(i){
        document.links[i].onclick = function(){
            alert(i); //Will alert 0, 1, ... 9
        }
    })(i);
}

【讨论】:

  • 谢谢。你的例子非常有趣和有用。我仍在试图弄清楚为什么它总是在第二个样本上提醒 9:)
  • @tugberk 因为循环从 0 走到 9,所以在每次枚举时更新变量 i。当用户单击链接时,将显示 i 变量 - 等于 9,每次更新。
  • 最后一个问题。这两个在DOM内部使用时是否相同:(function(){})();(function(w){})(window);
  • 1.为什么在这里使用函数表达式而不是函数声明? 2. 你称之为“闭包”的东西不是闭包。
  • 仅当您在第一个函数中定义变量w 时。
【解决方案2】:

请记住,函数参数和变量本质上是一回事。

第二个例子(基本上)只是简写

(function(){
    var d = document.documentElement;
}());

因为它避免了对var= 的需要。

这种模式有一些常见的用途:

  1. 创建词法范围的变量(在看到 Rob 的回答后才想起这一点......)

    //this does not work because JS only has function scope.
    // The i is shared so all the onclicks log N instead of the correct values
    for(var i = 0; i< N; i++){
       elems[i].onclick = function(){ console.log(i); }
    }
    
    //Each iteration now gets its own i variable in its own function
    // so things work fine.
    for(var i=0; i<N; i++){
       (function(i){
           elems[i].onclick = function{ console.log(i); };
       }(i));
    }
    

    在这种情况下,直接传递参数允许我们在内部重用相同的变量名,这是var i = i 无法做到的。此外,简洁是一个好处,因为这只是一个样板模式,我们不想支配它周围的重要代码。

  2. 它可以很容易地转换一些旧代码,而不必考虑太多

    (function($){
       //lots of code that expected $ to be a global...
    }(jQuery)) //and now we can seamlessly do $=jQuery instead.
    
  3. 未传递的参数设置为undefined。这很有用,因为通常undefined 只是一个可以设置为不同值的全局变量(如果您正在编写一个需要与任意第三方脚本一起工作的库,这一点尤其重要)

    (function(undefined){
       //...
    }())
    

【讨论】:

    【解决方案3】:

    第一个函数没有参数。第二个采用单个参数。在函数内部,参数是d。调用函数时d 设置为document.documentElement

    【讨论】:

      【解决方案4】:

      看起来第二个代码的作者想使用d作为在函数内部编写document.documentElement的更短的方式。

      【讨论】:

        【解决方案5】:

        如果您想将参数传递给自执行匿名函数,请使用第二个。当您想在函数中使用与全局范围内的其他变量同名的变量时,它可能会派上用场:

        var a = "I'm outside the function scope",
            b = 13;
        alert(a);
        alert(b);
        (function(a,b){
             // a is different from the initial a
             alert(a);
             // same goes for b
             alert(b);
        })("I'm inside the function scope",Math.PI);
        

        使用类似的东西也很有用:

        var a;
        console.log(a === undefined); // true
        undefined = true;
        console.log(a === undefined); // false
        (function(undefined){
            console.log(a === undefined); // true, no matter what value the variable "undefined" has assigned in the global scope
        })();
        

        当尝试实现null object design pattern

        如果您担心效率,您可能最终会使用这样的东西:

        var a = 2;
        (function(window){
            alert(a);
            // accessing a from the global scope gets translated 
            // to something like window.a, which is faster in this particular case
            // because we have the window object in the function scope
        })(window);
        

        【讨论】:

          【解决方案6】:

          使用带有参数的第二种形式的一个原因是,它将您的代码与页面上稍后加载的其他 js 代码(例如其他库或框架代码)隔离开来,这些代码可能会重新定义作为参数传入的变量。

          一个常见的例子是,如果您的自动执行匿名函数中的代码依赖于 jQuery,并且想要使用 $ 变量。

          其他 js 框架也定义了 $ 变量。如果您将函数编码为:

          (function($){
          
              //Code comes here
          
          })(jQuery);
          

          然后,即使您加载了其他定义 $ 的库,您也可以安全地将 $ 用于 jQuery。

          我已经看到人们在他们的块中传递他们需要的所有“全局”变量,例如窗口、文档、jQuery/$ 等。

          安全总比后悔好,尤其是在您使用大量第 3 方小部件和插件的情况下。

          更新:
          正如其他人指出的那样,函数周围的括号集是一个闭包。很多时候,在使用这种模式时,它们并不是严格必要的(@Rob W 给出了一个很好的例子,说明它们是必不可少的)但是说你有一个很长的函数体......外括号告诉其他阅读代码的人该功能可能是自动执行的。

          我在 Paul Irish 的视频中看到了关于这种模式的最佳解释:http://paulirish.com/2010/10-things-i-learned-from-the-jquery-source/ 大约 1:30 开始

          这个 SO 问题也有一些信息丰富的答案:How do you explain this structure in JavaScript?

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-10-29
            • 2011-02-20
            • 1970-01-01
            • 2015-03-08
            • 2020-05-14
            • 2014-05-12
            • 1970-01-01
            相关资源
            最近更新 更多