为什么这个函数包含在括号中,后跟括号?

Gar*_*hby 36 javascript function

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

(function() {

    //stuff

})();
Run Code Online (Sandbox Code Playgroud)

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

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

Rob*_*nik 26

这定义了一个函数闭包

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

请考虑以下代码:

(function(){
    var test = true;
})();
Run Code Online (Sandbox Code Playgroud)

变量test在其他任何地方都不可见,但在定义它的函数闭包内.

什么是关闭?

函数闭包使得各种脚本可以不相互干扰,即使它们定义了类似命名的变量或私有函数.这些私有部分是可见的,只能在封闭本身内访问,而不是在封闭之外.

检查此代码并阅读注释:

// 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"
Run Code Online (Sandbox Code Playgroud)

这是一个JSFiddle运行代码,显示数据,如上面代码中的注释所示.

它实际上做了什么?

你已经知道了

  1. 创建一个函数:

    function() { ... }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 并立即执行它:

    (func)();
    
    Run Code Online (Sandbox Code Playgroud)
  3. 此功能可能会也可能不会接受其他参数.

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

(function(paramName){ ... })(jQuery);
Run Code Online (Sandbox Code Playgroud)

但主要思想仍然是相同的:使用不能直接在其外部使用的私有定义来定义函数闭包.


sko*_*rks 13

这个结构被称为一个自动执行的匿名函数,它实际上并不是一个非常好的名称,这里发生了什么(以及为什么名称不是很好).这个:

function abc() {
    //stuff
}
Run Code Online (Sandbox Code Playgroud)

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

function() {
    //stuff
}
Run Code Online (Sandbox Code Playgroud)

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

function() {
    //stuff
}();
Run Code Online (Sandbox Code Playgroud)

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

(function() {
    //stuff
})();
Run Code Online (Sandbox Code Playgroud)

一个立即执行的匿名函数(你可以看到构造的名称有点偏,因为它不是一个真正执行自身的匿名函数,而是一个直接执行的匿名函数).

好吧,为什么所有这些都有用,一个原因是它允许您阻止代码污染全局命名空间.因为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
})();
Run Code Online (Sandbox Code Playgroud)

我们所有的变量和函数都是在我们自动执行的匿名函数中编写的,我们的代码首先执行,因为它在一个自执行的匿名函数中.并且由于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 );
    };
  })();
}
Run Code Online (Sandbox Code Playgroud)

基本上所有这一切都意味着这一点,人们经常写这样的天真的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] );
    };
}
Run Code Online (Sandbox Code Playgroud)

我们在循环中创建的函数是闭包,但不幸的是它们将锁定封闭范围的最后一个i(在这种情况下,它可能是2,这将导致麻烦).我们可能想要的是我们在循环中创建的每个函数在我们创建它时锁定i的值.这是我们自动执行匿名函数的地方,这里有一个类似但可能更容易理解的重写循环方式:

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

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

在那里,你有两个很好的理由使用自动执行的匿名函数结构以及为什么它实际上起作用.