【问题标题】:How does the (function() {})() construct work and why do people use it?(function() {})() 构造如何工作以及人们为什么使用它?
【发布时间】:2010-12-10 23:37:06
【问题描述】:

(function() {})() 和它的 jQuery 特定表亲 (function($) {})(jQuery) 一直在 Javascript 代码中弹出。

这些构造是如何工作的,它们解决了什么问题?

例子欣赏

【问题讨论】:

标签: javascript


【解决方案1】:

随着 JavaScript 框架的日益流行,$ 符号被用于许多不同的场合。因此,为了减轻可能的冲突,您可以使用这些构造:

(function ($){
  // Your code using $ here.
})(jQuery);

具体来说,这是一个匿名函数声明,它立即执行,并传递主jQuery对象作为参数。在该函数内部,您可以使用 $ 来引用该对象,而不必担心其他框架也在范围内。

【讨论】:

  • 简短而有意义的答案。 +1!
  • 它在技术上是一个函数表达式 (!declaration)。 IIFE
  • 你能解释一下你的意思吗:“......而不用担心其他框架也在范围内。”?
  • @Redtopia。 Javascript 范围是在函数级别定义的。函数之外的所有内容都在全局范围内。想想一个网页中包含了多少 Javascript。函数范围之外的每个变量都存在碰撞危险。
  • 感谢您的澄清...我不清楚您指的是匿名函数的本地范围,从全局范围或任何其他函数的范围都无法看到.
【解决方案2】:

这是一种用于限制变量范围的技术;这是防止变量污染全局命名空间的唯一方法。

var bar = 1; // bar is now part of the global namespace
alert(bar);

(function () {
   var foo = 1; // foo has function scope
   alert(foo); 
   // code to be executed goes here
})();

【讨论】:

  • ...并且相对于您的代码,它们仍然是“全局”的。
  • 那么示例代码中对 Opera 的引用是无关的/错误的?
  • @Bobby:该块中可能还有其他变量/函数仅用于 Opera。
【解决方案3】:

1) 它定义了一个匿名函数并立即执行。

2) 通常这样做是为了不让不需要的代码污染全局命名空间。

3) 你需要从中暴露一些方法,里面声明的任何东西都是“私有的”,例如:

MyLib = (function(){
    // other private stuff here
    return {
        init: function(){
        }
    };

})();

或者,或者:

MyLib = {};
(function({
    MyLib.foo = function(){
    }
}));

关键是,您可以使用多种方法,但结果保持不变。

【讨论】:

  • 1.这样的函数定义在一个单独的文件中,它是如何执行的? (好像没有人调用这个函数,那么这个函数是怎么触发的呢?) 2. 没看到顶层函数有“return”语句……我在看dojo.util._doh ._browserRunner.js 谢谢!
  • 1) 正如我所说,它自己执行,最后一个 () 告诉它执行。 2)它不一定必须有回报,它也可能在特定属性上设置值。这只是一个例子,我用另一种可能性更新了答案。
【解决方案4】:

它只是一个立即调用的匿名函数。你可以先创建函数然后调用它,你会得到同样的效果:

(function(){ ... })();

作为:

temp = function(){ ... };
temp();

你也可以对命名函数做同样的事情:

function temp() { ... }
temp();

您称为 jQuery-specific 的代码只是在您使用其中的 jQuery 对象的意义上。它只是一个带有参数的匿名函数,会立即被调用。

你可以分两步做同样的事情,你可以用任何你喜欢的参数来做:

temp = function(answer){ ... };
temp(42);

这解决的问题是它为函数中的代码创建了一个闭包。您可以在其中声明变量而不会污染全局命名空间,从而降低一个脚本与另一个脚本一起使用时发生冲突的风险。

在 jQuery 的特定情况下,您可以在兼容模式下使用它,它不会将名称 $ 声明为 jQuery 的别名。通过将 jQuery 对象发送到闭包中并将参数命名为 $,您仍然可以使用与不兼容模式相同的语法。

【讨论】:

  • 正如我对@spoulson 的回答所评论的,匿名函数与闭包不同。
【解决方案5】:

它解释了here,您的第一个构造为变量提供了范围。

在 javascript 中,变量的作用域是函数级别的。这与您在 C# 或 Java 等语言中所使用的不同,其中变量的作用域是块。这意味着如果您在循环或 if 语句中声明一个变量,它将可用于整个函数。

如果您发现自己需要在函数内显式定义变量范围,您可以使用匿名函数来执行此操作。您实际上可以创建一个匿名函数,然后立即执行它,其中的所有变量都将作用于匿名函数:

(function() {
  var myProperty = "hello world";
  alert(myProperty);
})();
alert(typeof(myProperty)); // undefined

【讨论】:

    【解决方案6】:

    这样做的另一个原因是消除对您正在使用哪个框架的$ 运算符的任何混淆。例如,要强制使用 jQuery,您可以这样做:

    ;(function($){
       ... your jQuery code here...
    })(jQuery);
    

    通过将$ 运算符作为参数传入并在jQuery 上调用它,即使您加载了其他框架,函数中的$ 运算符也会锁定到jQuery。

    【讨论】:

    • 这是对原始问题的更具体的变体,但它仍然是很好的信息! :)
    • 根本不是答案,但我已经为你的好主意加分了!
    【解决方案7】:

    此构造的另一个用途是“捕获”将在闭包中使用的局部变量的值。例如:

    for (var i = 0; i < 3; i++) {
        $("#button"+i).click(function() {
            alert(i);
        });
    }
    

    上面的代码会让三个按钮都弹出“3”。另一方面:

    for (var i = 0; i < 3; i++) {
        (function(i) {
            $("#button"+i).click(function() {
                alert(i);
            });
        })(i);
    }
    

    这将使三个按钮按预期弹出“0”、“1”和“2”。

    这样做的原因是闭包保留对其封闭堆栈frame的引用,该堆栈保存其变量的当前值。如果这些变量在闭包执行之前发生了变化,那么闭包将只看到最新的值,而不是创建闭包时的值。通过将闭包创建包装在 another 函数中,如上面第二个示例所示,变量 i 的当前值保存在匿名函数的堆栈帧中。

    【讨论】:

    • 一个小问题,但关闭的不是堆栈帧,而是周围的词法范围。好的闭包实现只会捕获闭包中实际提到的符号,有些实现只是捕获所有内容(Ruby 会这样做,所以要小心你在闭包附近有什么)。在 OOP 中考虑对象的一种方法是将捕获的符号明确定义为闭包(对象的成员属性)。
    • 确实如此,但这实际上只是一个实现问题,从概念上讲,可以认为堆栈帧是闭包引用的内容。 Lisp 家族语言中的 OOP 风格的“对象”都是用闭包完成的,这是一种强大的技术。
    • 是的,虽然如果你想从堆栈框架的角度来考虑,那么闭包就是一个堆栈框架链,一个框架用于词法范围内的每个封闭函数以及全局范围。词法作用域更容易考虑(尤其是在向不熟悉闭包的人解释时),因为当您有多个嵌套函数级别时,您不会就哪个堆栈框架进行毫无意义(和误导性)的讨论。词法范围就是您在源代码中看到的内容。
    【解决方案8】:

    这被视为closure。这意味着包含的代码将在其自己的词法范围内运行。这意味着您可以定义新的变量和函数,并且它们不会与闭包之外的代码中使用的命名空间发生冲突。

    var i = 0;
    alert("The magic number is " + i);
    
    (function() {
       var i = 99;
       alert("The magic number inside the closure is " + i);
    })();
    
    alert("The magic number is still " + i);
    

    这将生成三个弹出窗口,表明闭包中的i 不会改变预先存在的同名变量:

    • 幻数是 0
    • 闭包内的幻数是 99
    • 幻数还是0

    【讨论】:

    • 这不是一个闭包,这只是证明一个函数有它自己的作用域。当一个函数在另一个函数内部定义并且在该函数外部可用时,就会形成一个闭包,因此即使在外部函数返回之后,它仍然可以访问外部函数中的变量、函数和参数。
    • 只需将“(function() {...”更改为“var func = function() {...}; func();”即可。关闭。
    • 我不想做这个,但是......仍然没有关闭:没有在外部函数之外创建内部函数。在您的示例中创建闭包的一种简单方法是返回一个警告 i 的函数: var f = (function() { var i = 99; return function() { alert(i); }; })(); f();
    • 注意点。它归结为实现的细节。
    【解决方案9】:

    它们经常用于 jQuery 插件中。正如jQuery Plugins Authoring Guide 中所解释的,在{ } 中声明的所有变量都是私有的,对外不可见,这样可以更好地封装。

    【讨论】:

    • {} 本身并不会在 JavaScript 中创建新范围。作用域由函数声明创建。
    【解决方案10】:

    正如其他人所说,它们都定义了立即调用的匿名函数。我通常将我的 JavaScript 类声明包装在这个结构中,以便为该类创建一个静态私有作用域。然后我可以在该范围内放置常量数据、静态方法、事件处理程序或其他任何东西,并且它只对类的实例可见:

    // Declare a namespace object.
    window.MyLibrary = {};
    
    // Wrap class declaration to create a private static scope.
    (function() {
      var incrementingID = 0;
    
      function somePrivateStaticMethod() {
        // ...
      }
    
      // Declare the MyObject class under the MyLibrary namespace.
      MyLibrary.MyObject = function() {
        this.id = incrementingID++;
      };
    
      // ...MyObject's prototype declaration goes here, etc...
      MyLibrary.MyObject.prototype = {
        memberMethod: function() {
          // Do some stuff
          // Maybe call a static private method!
          somePrivateStaticMethod();
        }
      };
    })();
    

    在这个例子中,MyObject 类被分配给MyLibrary 命名空间,所以它是可访问的。 incrementingIDsomePrivateStaticMethod() 在匿名函数范围之外不能直接访问。

    【讨论】:

      【解决方案11】:

      这基本上是 namespace 你的 JavaScript 代码。

      例如,您可以在其中放置任何变量或函数,而从外部来看,它们不存在于该范围内。因此,当您将所有内容封装在其中时,您不必担心冲突。

      末尾的() 表示自调用。您还可以在此处添加一个参数,该参数将成为您的匿名函数的参数。我经常用 jQuery 来做这件事,你可以明白为什么......

      (function($) {
      
          // Now I can use $, but it won't affect any other library like Prototype
      })(jQuery);
      

      Evan Trimboli 涵盖了他的answer 中的其余部分。

      【讨论】:

        【解决方案12】:

        这是一个自调用函数。有点像写作的速记

        function DoSomeStuff($)
        {
        }
        
        DoSomeStuff(jQuery);
        

        【讨论】:

          【解决方案13】:

          上面的代码所做的是在第 1 行创建一个匿名函数,然后在第 3 行使用 0 个参数调用它。这有效地封装了该库中定义的所有函数和变量,因为所有函数都只能在该匿名函数内部访问。

          这是一种很好的做法,其背后的原因是避免变量和函数污染全局命名空间,这些变量和函数可能会被整个站点的其他 Javascript 片段破坏。

          为了阐明函数是如何被调用的,考虑一个简单的例子:

          如果您包含这行 Javascript,它将自动调用而不显式调用它:

          alert('hello');
          

          所以,把这个想法应用到这个例子中:

          (function() {
              alert('hello')
              //anything I define in here is scoped to this function only
          }) (); //here, the anonymous function is invoked
          

          最终结果是相似的,因为匿名函数的调用就像前面的例子一样。

          【讨论】:

          • 请看我对埃文的 cmets 的第一个问题...谢谢!
          • //here, the anonymous function is invoked,很简单,不过之前没见过:)
          【解决方案14】:

          因为 good code 答案已经被采纳 :) 我会建议观看一些 John Resig 视频 video 1 , video 2 (jQuery 的发明者和 JavaScript 大师)。

          视频中提供了一些非常好的见解和答案。

          当我看到你的问题时,我恰好在做这件事。

          【讨论】:

            【解决方案15】:
            function(){ // some code here }
            

            是在javascript中定义匿名函数的方式。它们可以使您能够在另一个函数的上下文中执行一个函数(否则您可能没有该能力)。

            【讨论】:

              猜你喜欢
              • 2011-07-04
              • 2016-08-18
              • 1970-01-01
              • 2018-01-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多