关于javascript:为什么这个函数用括号括起来,后面跟着括号?

Why is this function wrapped in parentheses, followed by parentheses?

本问题已经有最佳答案,请猛点这里访问。

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

1
2
3
4
5
(function() {

    //stuff

})();

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

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


这定义了一个函数闭包

这用于创建一个函数闭包,其中包含私有功能和不全局可见的变量。

请考虑以下代码:

1
2
3
(function(){
    var test = true;
})();

变量test在其他任何地方都不可见,而是在定义它的函数闭包中。

什么是关闭?

函数闭包使得不同的脚本不相互干扰成为可能,即使它们定义了相似的命名变量或私有函数。这些私人空间是可见的,只能在封闭区内进入,而不能在封闭区外进入。

检查此代码并阅读注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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的代码,它按照上面代码中的注释显示数据。

它实际上做了什么?

你已经知道了

  • 创建函数:

    1
    function() { ... }
  • 立即执行:

    1
    (func)();
  • 此函数可能接受或不接受其他参数。

  • jquery插件通常是这样定义的,通过用一个插件在其中操作的参数定义一个函数:

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

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


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

    1
    2
    3
    function abc() {
        //stuff
    }

    定义一个名为abc的函数,如果我们想要一个匿名函数(在javascript中这是一个非常常见的模式),它应该是沿着以下几行的:

    1
    2
    3
    function() {
        //stuff
    }

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

    1
    2
    3
    function() {
        //stuff
    }();

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

    1
    2
    3
    (function() {
        //stuff
    })();

    一个立即执行的匿名函数(您可以看到构造的名称有点偏离,因为它实际上不是一个执行自身的匿名函数,而是一个直接执行的匿名函数)。

    好吧,那么为什么所有这些都有用,一个原因是它允许您阻止代码污染全局名称空间。因为JavaScript中的函数有自己的作用域,所以函数中的任何变量在全局上都是不可见的,所以如果我们能够在函数中编写所有代码,那么全局作用域是安全的,那么我们的自执行匿名函数就允许我们这样做。让我借用约翰·雷西格的旧书中的一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 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中使用闭包时有时会出现的问题。我再次让约翰·雷西格解释,我引用:

    Remember that closures allow you to reference variables that exist
    within the parent function. However, it does not provide the value of
    the variable at the time it is created; it provides the last value of
    the variable within the parent function. The most common issue under
    which you’ll see this occur is during a for loop. There is one
    variable being used as the iterator (e.g., i). Inside of the for loop,
    new functions are being created that utilize the closure to reference
    the iterator again. The problem is that by the time the new closured
    functions are called, they will reference the last value of the
    iterator (i.e., the last position in an array), not the value that you
    would expect. Listing 2-16 shows an example of using anonymous
    functions to induce scope, to create an instance where expected
    closure is possible.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 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代码(这是上面的循环的幼稚版本):

    1
    2
    3
    4
    5
    6
    7
    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] );
        };
    }

    我们在循环中创建的函数是闭包,但不幸的是,它们将从封闭范围锁定i的最后一个值(在本例中,可能是2,这将导致问题)。我们可能需要的是在循环中创建的每个函数在创建时锁定i的值。这就是我们的自执行匿名函数出现的地方,这里有一个类似但可能更容易理解的重写循环的方法:

    1
    2
    3
    4
    5
    6
    7
    for ( var i = 0; i < items.length; i++ ) {
      (function(index){
        obj["on" + item ] = function() {
          alert("Thanks for your" + items[index] );
        };
      })(i);
    }

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

    现在,有两个很好的理由来使用自执行匿名函数构造,以及为什么它首先实际工作。


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

    如果您想在适当的地方定义一个一次性函数,然后立即调用它,那么它是很有用的。使用匿名函数和只写代码的区别在于范围。当函数结束时,匿名函数中的所有变量都将超出作用域(当然,除非变量被告知其他情况)。这可以用来保持全局或封闭的名称空间的清洁,长期使用较少的内存,或者获得一些"隐私"。


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

    在JavaScript中,只有函数引入了新的作用域。通过将代码包装在即时函数中,您定义的所有变量只存在于这个或更低的范围内,而不存在于全局范围内。

    所以这是一个不污染全球范围的好方法。

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


    当您想要创建一个闭包时,这样的构造是有用的——一个构造有助于为从外部无法访问的变量创建一个私有的"房间"。请参阅"javascript:The Good Parts"一章中的更多内容:http://books.google.com/books?id=pxa2bby0oq0c&pg=pa37&plp=pa37&dq=crockford+clockford+clocloclockcal+立即调用&source=bl&ots=hilk8x4jl&sig=-t-t0jtmf7 twzacq5_5aj3a&hl=lv&;ei=Lsa5tax x x x x x x x&sa=x&;oi=book-result&ct=res结果&resnum=1&;ved=0buq6aewaa \瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦v=一页&q&f=假

    在第38页顶部所示的示例中,您可以看到变量"status"隐藏在一个闭包中,除了调用get_status()方法之外,无法访问它。


    它是"匿名自执行函数"或"立即调用函数表达式"。本·阿尔曼的解释很好。

    创建名称空间时使用该模式

    1
    2
    3
    4
    5
    6
    7
    var APP = {};

    (function(context){



    })(APP);