(function(){})()构造如何工作,人们为什么使用它?


79

(function() {})() 及其特定于jQuery的表弟 (function($) {})(jQuery)一直以Javascript代码弹出。

这些构造如何工作,它们可以解决什么问题?

实例赞赏



2
这不是完全相同的副本。其他问题的答案并没有真正说明为什么一个人提供jQuery了论据。
格奥尔格·舍利

1
我一直想知道为什么需要额外的括号。我认为这与花括号和对象文字之间的歧义有关。请注意,如果您要分配某些内容,则不需要它们,例如var x = function(){}()可以正常工作。
jpsimons

1
为什么这个“封闭的不是真正的问题”?这个问题是明确和相关的。
克里斯(Chris)

Answers:


73

随着JavaScript框架的日益普及,该$标志已在许多不同的场合使用。因此,为减轻可能的冲突,可以使用以下构造:

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

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


4
简短而有意义的答案。+1!
pestaa

5
从技术上讲,它是一个函数表达式(!声明)。 IIFE
John Strickler,

您能解释一下您的意思吗:“ ...而不必担心其他框架也在范围之内。”?
Redtopia 2012年

@Redtopia。Javascript范围是在功能级别定义的。函数外的所有内容都在全局范围内。考虑一下网页中包含多少个Javascript。函数范围之外的每个变量都充满冲突。
阿尔贝托·德卡罗

感谢您的澄清...对我来说,您目前所指的是匿名函数的本地范围,这从全局范围或任何其他函数的范围中都看不到。
Redtopia 2012年

41

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

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

7
...并且相对于您的代码,它们仍然是“全局的”。
tvanfosson,2009年

因此,示例代码中对Opera的引用是不相关/错误的吗?
鲍比·杰克

1
@Bobby:该块中可能还有其他变量/函数,仅用于Opera。
geowa4,2009年

19

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)正如我说的,它自己执行,last()告诉它执行。2)不一定必须有返回值,它可能还会在特定属性上设置值。这只是一个例子,我用另一种可能性更新了答案。
埃文·特里波利

9

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

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

用作:

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

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

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

所谓的jQuery特定代码只是在其中使用jQuery对象的意义上。它只是一个带有参数的匿名函数,将立即被调用。

您可以分两步执行相同的操作,并且可以使用任意所需的参数来执行此操作:

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

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

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


正如我评论@spoulson的答案一样,匿名函数与闭包并不相同。
Tim Down

7

它解释这里是你的第一个结构变量提供空间。

变量在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。


这是原始问题的一个更具体的变化形式,但它仍然是一个很好的信息!:)
Bobby Jack

根本没有答案,但是我已经为您的宏伟构想提高了答案!
Spidey

5

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

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”。

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


一个小巧的选择,但是不是封闭的堆栈框架,而是周围的词汇作用域。闭包的良好实现只能捕获闭包内部实际提到的符号,有些实现只是捕获所有内容(Ruby会这样做,因此请注意闭包附近的内容)。在OOP中考虑对象的一种方法是闭包,其中明确定义了捕获的符号(对象的成员属性)。
KayEss

没错,但这实际上只是一个实现问题,从概念上讲,可以考虑栈框架是闭包引用的内容。Lisp家族语言中的OOP风格的“对象”都是用闭包完成的,这是一项强大的技术。
2009年

是的,尽管如果您想从堆栈框架的角度考虑,那么闭包是一堆堆栈框架,词汇范围和全局范围中的每个封闭函数都需要一个框架。词法范围更容易考虑(尤其是在向不熟悉闭包的人进行解释时),因为当您具有多层嵌套函数时,您不会陷入关于哪个堆栈框架的毫无意义(且具有误导性)的讨论。词法作用域就是您在源代码中看到的。
KayEss

4

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

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

5
那不是闭包,只是表明一个函数有自己的作用域。当在另一个函数内部定义一个函数并在该函数外部可用时,就会形成一个闭包,从而即使在该外部函数返回后仍保留对外部函数中变量,函数和参数的访问。
Tim Down

只需将“((function(){...”)更改为“ var func = function(){...}; func();”。),在那里,关闭
。– spoulson

我不想为此烦恼,但是...仍然没有闭包:没有在外部功能之外制造内部功能。在您的示例中创建闭合的一种简单方法是返回一个警告i的函数:var f =(function(){var i = 99; return function(){alert(i);};})(); F();
Tim Down

点指出。它取决于实现的细节。
spoulson


2

正如其他人所说,它们都定义了立即调用的匿名函数。我通常将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名称空间,因此可以访问。 incrementingID并且somePrivateStaticMethod()不能在匿名函数范围之外直接访问。


2

这基本上是为您的JavaScript代码命名空间

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

()在年底手段自我调用。您也可以在其中添加一个参数,该参数将成为匿名函数的参数。我经常使用jQuery来执行此操作,您会明白为什么...

(function($) {

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

埃文·特里波利(Evan Trimboli)回答了其余的问题


1

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

function DoSomeStuff($)
{
}

DoSomeStuff(jQuery);

1

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

这是一个好习惯,其背后的原因是避免用变量和函数污染全局名称空间,而变量和函数可能会在整个站点中被其他Javascript所破坏。

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

如果您包含这行Java语言,它将自动调用而无需显式调用它:

alert('hello');

因此,请采纳该想法并将其应用于以下示例:

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

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


请参阅我对Evan发表评论的第一个问题...谢谢!
保罗

//here, the anonymous function is invoked,简单,但是我之前没看过它:)
Stefan

1

因为已经采取了良好的 代码答案:)我建议您看一些John Resig视频video 1video 2(jQuery的发明者和JavaScript的主人)。

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

那是我看到您的问题时碰巧所做的事情。


0
function(){ // some code here }

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.