关于javascript:(function(){})()构造如何工作以及人们为什么使用它?

How does the (function() {})() construct work and why do people use it?

(function() {})()及其jQuery特定的表兄(function($) {})(jQuery)一直在Javascript代码中弹出。

这些结构如何工作,它们解决了哪些问题?

举例赞赏


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

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

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


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

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


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

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

3)你需要从中公开一些方法,在里面声明的任何东西都是"私有的",例如:

1
2
3
4
5
6
7
8
MyLib = (function(){
    // other private stuff here
    return {
        init: function(){
        }
    };

})();

或者,或者:

1
2
3
4
5
MyLib = {};
(function({
    MyLib.foo = function(){
    }
}));

关键是,有很多方法可以使用它,但结果保持不变。


它只是一个立即调用的匿名函数。您可以先创建该函数,然后调用它,您将获得相同的效果:

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

作为:

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

您也可以使用命名函数执行相同操作:

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

您调用jQuery特定的代码只是在您使用jQuery对象的意义上。它只是一个带有参数的匿名函数,可以立即调用。

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

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

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

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


这里解释说,您的第一个构造为变量提供了范围。

Variables are scoped at the function level in javascript. This is different to what you might be used to in a language like C# or Java where the variables are scoped to the block. What this means is if you declare a variable inside a loop or an if statement, it will be available to the entire function.

If you ever find yourself needing to explicitly scope a variable inside a function you can use an anonymous function to do this. You can actually create an anonymous function and then execute it straight away and all the variables inside will be scoped to the anonymous function:

1
2
3
4
5
(function() {
  var myProperty ="hello world";
  alert(myProperty);
})();
alert(typeof(myProperty)); // undefined

执行此操作的另一个原因是消除您正在使用的框架的$运算符的任何混淆。例如,要强制执行jQuery,您可以执行以下操作:

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

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


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

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

上面的代码将使所有三个按钮弹出"3"。另一方面:

1
2
3
4
5
6
7
for (var i = 0; i < 3; i++) {
    (function(i) {
        $("#button"+i).click(function() {
            alert(i);
        });
    })(i);
}

这将使三个按钮按预期弹出"0","1"和"2"。

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


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

1
2
3
4
5
6
7
8
9
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


这基本上是命名您的JavaScript代码。

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

最后的()表示自我调用。您还可以在那里添加一个参数,它将成为您的匿名函数的参数。我经常用jQuery做这个,你可以看到为什么......

1
2
3
4
(function($) {

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

Evan Trimboli在答案中介绍了其余部分。


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

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
// 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()不能在匿名函数范围之外直接访问。


它们经常用在jQuery插件中。正如在jQuery插件创作指南中所解释的,在{ }内声明的所有变量都是私有的,并且对外部不可见,这允许更好的封装。


因为已经采取了良好的代码答案:)我将提出建议观看一些John Resig视频视频1,视频2(jQuery的创建者和JavaScript的高手)。

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

这就是我在看到你的问题时碰巧做的事情。


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

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

为了阐明函数的调用方式,请考虑以下简单示例:

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

1
alert('hello');

所以,采取这个想法,并将其应用于此示例:

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

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


这是一个自我调用的功能。有点像写作的简写

1
2
3
4
5
function DoSomeStuff($)
{
}

DoSomeStuff(jQuery);

1
function(){ // some code here }

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