【问题标题】:Why is this function wrapped in parentheses, followed by parentheses? [duplicate]为什么这个函数用括号括起来,后跟括号? [复制]
【发布时间】:2011-08-14 12:26:09
【问题描述】:

我一直在 javascript 源代码中看到这一点,但我从未真正发现使用此构造的真正原因。为什么需要这个?

(function() {

    //stuff

})();

为什么会这样写?为什么不单独使用stuff 而不是在函数中使用?

编辑:我知道这是定义一个匿名函数然后调用它,但是为什么

【问题讨论】:

  • +1 我想知道很久了。希望得到一个有根据的答案!

标签: javascript function


【解决方案1】:

这定义了一个函数闭包

这用于创建具有私有功能和全局不可见变量的函数闭包。

考虑以下代码:

(function(){
    var test = true;
})();

变量test 在定义它的函数闭包内不可见。

什么是闭包?

函数闭包可以让各种脚本不相互干扰,即使它们定义了类似命名的变量或私有函数。这些私有数据仅在闭包内部而不是在闭包外部可见和可访问。

检查此代码并连同它一起阅读 cmets:

// public part
var publicVar = 111;
var publicFunc = function(value) { alert(value); };
var publicObject = {
    // no functions whatsoever
};

    // closure part
    (function(pubObj){
        // private variables and functions
        var closureVar = 222;
        var closureFunc = function(value){
            // call public func
            publicFunc(value);
            // alert private variable
            alert(closureVar);
        };

        // add function to public object that accesses private functionality
        pubObj.alertValues = closureFunc;

        // mind the missing "var" which makes it a public variable
        anotherPublic = 333;

    })(publicObject);

// alert 111 & alert 222
publicObject.alertValues(publicVar);

// try to access varaibles
alert(publicVar); // alert 111
alert(anotherPublic); // alert 333
alert(typeof(closureVar)); // alert "undefined"

这是一个JSFiddle running code,它显示上面代码中 cmets 指示的数据。

它实际上做了什么?

你已经知道了

  1. 创建一个函数:

    function() { ... }
    
  2. 并立即执行:

    (func)();
    
  3. 此函数可能接受也可能不接受附加参数。

jQuery 插件通常以这种方式定义,通过定义一个带有插件在其中操作的参数的函数:

(function(paramName){ ... })(jQuery);

但主要思想还是一样的:用私有定义定义一个函数闭包,不能在它之外直接使用。

【讨论】:

    【解决方案2】:

    该构造被称为自执行匿名函数,实际上这不是一个很好的名字,这就是发生的事情(以及为什么这个名字不是一个好名字)。这个:

    function abc() {
        //stuff
    }
    

    定义一个名为abc的函数,如果我们想要一个匿名函数(这是javascript中非常常见的模式),它会是这样的:

    function() {
        //stuff
    }
    

    但是,如果你有这个,你要么需要将它与一个变量相关联,以便你可以调用它(这将使它不那么匿名),要么你需要立即执行它。我们可以尝试通过这样做立即执行它:

    function() {
        //stuff
    }();
    

    但这不起作用,因为它会给你一个语法错误。您收到语法错误的原因如下。当你创建一个有名字的函数时(比如上面的abc),这个名字就变成了一个函数表达式的引用,你就可以在后面加上()来执行这个表达式名称例如:abc()。声明函数的行为不会创建表达式,函数声明实际上是语句而不是表达式。本质上,表达式是可执行的,而语句不是(您可能已经猜到了)。所以为了执行一个匿名函数,你需要告诉解析器它是一个表达式而不是一个语句。 一种这样做的方法(不是唯一的方法,但它已成为惯例)是将您的匿名函数包装在一组 () 中,这样您就得到了您的构造:

    (function() {
        //stuff
    })();
    

    立即执行的匿名函数(您可以看到该构造的名称有点偏离,因为它并不是真正执行自身的匿名函数,而是立即执行的匿名函数)。

    好的,为什么这一切有用,一个原因是它可以让您阻止代码污染全局命名空间。因为 javascript 中的函数有自己的作用域,所以函数中的任何变量在全局范围内都是不可见的,所以如果我们能够以某种方式将所有代码写入函数内部,那么全局范围将是安全的,我们的自执行匿名函数允许我们这样做.让我从 John Resig 的旧书中借用一个例子:

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

    我们所有的变量和函数都写在我们的自执行匿名函数中,我们的代码首先被执行,因为它是在自执行匿名函数中。由于 javascript 允许闭包,即本质上允许函数访问定义在外部函数中的变量,我们几乎可以在自执行匿名函数中编写我们喜欢的任何代码,并且一切仍将按预期工作。

    但是等等还有更多:)。这种结构允许我们解决在 javascript 中使用闭包时有时会出现的问题。我将再次让 John Resig 解释一下,我引用:

    请记住,闭包允许您引用存在的变量 在父函数中。但是,它不提供价值 创建时的变量;它提供了最后一个值 父函数中的变量。下最常见的问题 你会看到这是在 for 循环期间发生的。有一个 用作迭代器的变量(例如,i)。在 for 循环内部, 正在创建利用闭包来引用的新函数 再次迭代器。问题是,当新的关闭时 函数被调用,它们将引用最后一个值 迭代器(即数组中的最后一个位置),而不是您的值 会期望。清单 2-16 显示了一个使用匿名的示例 诱导作用域的功能,在预期的地方创建一个实例 关闭是可能的。

    // An element with an ID of main
    var obj = document.getElementById("main");
    
    // An array of items to bind to
    var items = [ "click", "keypress" ];
    
    // Iterate through each of the items
    for ( var i = 0; i < items.length; i++ ) {
      // Use a self-executed anonymous function to induce scope
      (function(){
        // Remember the value within this scope
        var item = items[i];
        // Bind a function to the element
        obj[ "on" + item ] = function() {
          // item refers to a parent variable that has been successfully
          // scoped within the context of this for loop
          alert( "Thanks for your " + item );
        };
      })();
    }
    

    基本上所有这些都意味着,人们经常像这样编写幼稚的 javascript 代码(这是上面循环的幼稚版本):

    for ( var i = 0; i < items.length; i++ ) {
        var item = items[i];
        // Bind a function to the elment
        obj[ "on" + item ] = function() {
          alert( "Thanks for your " + items[i] );
        };
    }
    

    我们在循环中创建的函数是闭包,但不幸的是,它们会锁定封闭范围内ilast 值(在这种情况下,它可能是 2,这将导致麻烦)。我们可能希望在循环中创建的每个函数在创建时锁定 i 的值。这就是我们的自执行匿名函数的用武之地,这里有一种类似但可能更容易理解的重写循环的方法:

    for ( var i = 0; i < items.length; i++ ) {
      (function(index){
        obj[ "on" + item ] = function() {
          alert( "Thanks for your " + items[index] );
        };
      })(i);
    }
    

    因为我们在每次迭代中调用我们的匿名函数,所以我们传入的参数被锁定为传入时的值,因此我们在循环中创建的所有函数都将按预期工作。

    你去吧,使用自执行匿名函数构造的两个很好的理由,以及它为什么首先真正起作用。

    【讨论】:

    • 实际上还有第三个原因,这与不污染全局命名空间有些相关,但本质上允许您创建带有私有变量的对象。如果有人有兴趣,我可以提供更多详细信息。
    【解决方案3】:

    用于定义一个匿名函数,然后调用它。我没有尝试过,但我最好的猜测是为什么块周围有括号是因为 JavaScript 需要它们来理解函数调用。

    如果您想在适当的位置定义一次性函数,然后立即调用它,这很有用。使用匿名函数和仅仅写出代码之间的区别在于作用域。当函数结束时,匿名函数中的所有变量都将超出范围(当然,除非另有说明)。这可用于保持全局或封闭命名空间清洁、长期使用更少的内存或获得一些“隐私”。

    【讨论】:

    • 问题是他们为什么这样做而不是这样做。
    • @Rufinus 如果您继续阅读,我在回答中提供了一个相当详细的想法。
    • 当我评论它时在那儿 :)
    • @Rufinus 的编辑就是这么棘手。我的回答是预操作编辑。
    【解决方案4】:

    它是一个“匿名自执行函数”或“立即调用函数表达式”。 Ben Alman here 的精彩解释。

    我在创建命名空间时使用该模式

    var APP = {};
    
    (function(context){
    
    
    
    })(APP);
    

    【讨论】:

    • 为什么要把APP var传回函数?
    • 将值锁定到闭包中
    • 当你的意思是把值锁回封装中时,你是说把值锁回用函数(上下文){}创建的命名空间,正确。
    【解决方案5】:

    当您想要创建闭包时,这样的构造很有用 - 构造有助于为无法从外部访问的变量创建一个私有“房间”。在“JavaScript:好的部分”一书的这一章中查看更多信息: http://books.google.com/books?id=PXa2bby0oQ0C&pg=PA37&lpg=PA37&dq=crockford+closure+called+immediately&source=bl&ots=HIlku8x4jL&sig=-T-T0jTmf7_p_6twzaCq5_5aj3A&hl=lv&ei=lSa5TaXeDMyRswa874nrAw&sa=X&oi=book_result&ct=result&resnum=1&ved=0CBUQ6AEwAA#v=onepage&q&f=false

    在第 38 页顶部显示的示例中,您会看到变量“status”隐藏在闭包中,除了调用 get_status() 方法之外无法访问。

    【讨论】:

    • 参考书中很好的解释。这是我一直想读的。
    • @Gary:它比理解这个概念所需的时间更长、更详细。
    【解决方案6】:

    我不确定这个问题是否已经回答,如果我只是重复一些东西,请道歉。

    在 JavaScript 中,只有函数会引入新的范围。通过将您的代码包装在一个立即函数中,您定义的所有变量都只存在于这个或更低的范围内,但不在全局范围内

    所以这是不污染全局范围的好方法。

    应该只有几个全局变量。请记住,每个全局都是window 对象的一个​​属性,默认情况下它已经有很多属性。引入新范围还可以避免与 window 对象的默认属性发生冲突。

    【讨论】:

      猜你喜欢
      • 2012-02-21
      • 1970-01-01
      • 1970-01-01
      • 2016-07-21
      • 2013-06-16
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      相关资源
      最近更新 更多