将整个Javascript文件包装在“(function(){...})()”之类的匿名函数中的目的是什么?

时间:2010-03-11 01:20:02

标签: javascript scope coding-style iife

我最近一直在阅读大量的Javascript,并且我注意到整个文件在要导入的.js文件中包含如下内容。

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

这样做的原因是什么,而不是一组简单的构造函数?

8 个答案:

答案 0 :(得分:760)

通常是命名空间(见后面)并控制成员函数和/或变量的可见性。把它想象成一个对象定义。 jQuery插件通常都是这样编写的。

在Javascript中,您可以嵌套函数。所以,以下是合法的:

function outerFunction() {
   function innerFunction() {
      // code
   }
}

现在您可以致电outerFunction(),但innerFunction()的可见性仅限于outerFunction()的范围,这意味着outerFunction()是私有的。它基本上遵循与Javascript中的变量相同的原则:

var globalVariable;

function someFunction() {
   var localVariable;
}

相应地:

function globalFunction() {

   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };

   function localFunction2() {
      //I'm named!
   }
}

在上述情况中,您可以随时随地拨打globalFunction(),但无法拨打localFunction1localFunction2

当你写(function() { ... code ... })()时你正在做什么,你是在函数文字中创建代码(意味着整个“对象”实际上是一个函数)。之后,您将自行调用该函数(最终())。因此,我之前提到的主要优点是,您可以拥有私有方法/功能和属性:

(function() {
   var private_var;

   function private_function() {
     //code
   }
})()

在第一个示例中,globalFunction()是可以调用以访问公共功能的公共函数,但在上面的示例中,您如何调用它?这里的自调用函数使代码在启动时自动运行。就像你可以添加initMyStuff();在任何.js文件的顶部,它将自动作为全局范围的一部分运行,这个自调用函数也将自动运行,虽然因为它是一个未命名的函数,所以它不能像initMyStuff()那样被多次调用。 / p>

巧妙的是,您还可以在内部定义内容并将其公开给外部世界(例如命名空间,以便您基本上可以创建自己的库/插件):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()

现在您可以致电myPlugin.public_function1(),但无法访问private_function()!非常类似于类定义。为了更好地理解这一点,我推荐以下链接进行进一步阅读:

修改

我忘了提。在最后的()中,您可以传递任何您想要的内容。例如,当您创建jQuery插件时,您可以像这样传递jQuery$

(function(jQ) { ... code ... })(jQuery) 

所以你在这里做的是定义一个函数,它接受一个参数(称为jQ,一个局部变量,并且只知道 到该函数)。然后你自己调用函数并传入一个参数(也称为jQuery,但这个一个来自外部世界,并引用实际的jQuery本身)。没有迫切需要这样做,但有一些优点:

  • 您可以重新定义全局参数,并为其指定在本地范围内有意义的名称。
  • 稍微有一点性能优势,因为在本地范围内查找内容更快,而不必将范围链推向全局范围。
  • 压缩(缩小)有好处。

前面我描述了这些函数在启动时是如何自动运行的,但是如果它们自动运行谁传递了参数?该技术假设所有参数都定义为全局变量。因此,如果jQuery未被定义为全局变量,则此示例将无效,并且由于我们的示例是匿名函数,因此无法以任何其他方式调用。正如您可能猜到的,jquery.js在初始化过程中所做的一件事是定义一个'jQuery'全局变量,以及它更着名的'$'全局变量,它允许此代码在包含jquery.js之后工作。

答案 1 :(得分:75)

简而言之

摘要

在最简单的形式中,此技术旨在将代码包装在函数范围中。

它有助于降低:

的机会
  • 与其他应用程序/库冲突
  • 污染上级(全球最可能)范围

没有检测文档何时准备就绪 - 它不是某种document.onload也不是window.onload

通常称为Immediately Invoked Function Expression (IIFE)Self Executing Anonymous Function

代码说明

var someFunction = function(){ console.log('wagwan!'); };

(function() {                   /* function scope starts here */
  console.log('start of IIFE');

  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

在上面的例子中,函数中定义的任何变量(即使用var声明)将是" private"并且只能在功能范围内访问(正如Vivin Paliath所说)。换句话说,这些变量在函数外部是不可见/可达的。 See live demo

Javascript具有功能范围。 "函数中定义的参数和变量在函数外部是不可见的,函数内任何位置定义的变量在函数内的任何地方都可见。" (来自" Javascript:好的部分")。


更多细节

替代代码

最后,之前发布的代码也可以按如下方式完成:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};

myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

See live demo


The Roots

迭代1

总有一天,有人可能会想到"必须有办法避免命名myMainFunction',因为我们想要的就是立即执行它。"

如果你回到基础,你会发现:

  • expression:评估某个值的内容。即3+11/x
  • statement:代码行做某事但是评估为某个值。即if(){}

类似地,函数表达式求值为一个值。一个结果(我假设?)是可以立即调用它们:

 var italianSayinSomething = function(){ console.log('mamamia!'); }();

所以我们更复杂的例子变成了:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

See live demo

迭代2

下一步是思考"为什么var myMainFunction =如果我们甚至不使用它??"。

答案很简单:尝试删除它,如下所示:

 function(){ console.log('mamamia!'); }();

See live demo

它不会起作用,因为"函数声明不可调用"

诀窍是,通过删除var myMainFunction =,我们将函数表达式转换为函数声明。请参阅"资源"中的链接。有关详细信息。

接下来的问题是"为什么我不能将它作为函数表达式而不是var myMainFunction =

答案是"您可以",实际上有很多方法可以做到这一点:添加+!-或者也许包裹在一对括号中(因为它现在通过惯例完成),我更相信。例如:

 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.

 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console

 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console

因此,一旦将相关修改添加到我们的"替代代码"之后,我们将返回与"代码解释"中使用的代码完全相同的代码。示例

var someFunction = function(){ console.log('wagwan!'); };

(function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

详细了解Expressions vs Statements


揭秘范围

有人可能想知道的是,当你没有正确地定义变量时,会发生什么?'在函数内部 - 即做一个简单的赋值?"

(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the console

See live demo

基本上,如果未在当前作用域中声明的变量被赋值,那么"查找作用域链直到它找到变量或达到全局作用域(此时它将创建)它)"

在浏览器环境中(与nodejs等服务器环境相比),全局范围由window对象定义。因此我们可以window.myOtherFunction()

我的"良好做法"关于此主题的提示是在定义任何内容时始终使用var :它是数字,对象还是函数,&即使在全球范围内。这使代码更加简单。

注意:

  • javascript block scope(更新:ES6中添加的块范围局部变量。)
  • javascript只有function scope& global scope(浏览器环境中的window范围)

详细了解Javascript Scopes


资源


后续步骤

获得此IIFE概念后,它会导致module pattern,这通常是通过利用此IIFE模式完成的。玩得开心:))

答案 2 :(得分:26)

浏览器中的Javascript只有几个有效的范围:功能范围和全局范围。

如果变量不在函数范围内,则它在全局范围内。全局变量通常都很糟糕,因此这是一个将库的变量保存到自身的构造。

答案 3 :(得分:19)

这叫做封闭。它基本上密封了函数内部的代码,以便其他库不会干扰它。它类似于用编译语言创建命名空间。

实施例。假设我写道:

(function() {

    var x = 2;

    // do stuff with x

})();

现在其他库无法访问我创建的变量x以在我的库中使用。

答案 4 :(得分:8)

您也可以在较大的表达式中将函数闭包用作数据,就像在确定浏览器对某些html5对象的支持的方法一样。

   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }

答案 5 :(得分:7)

除了将变量保持为局部之外,一个非常方便的用途是在使用全局变量编写库时,可以为其提供一个较短的变量名,以便在库中使用。它经常用于编写jQuery插件,因为jQuery允许你使用jQuery.noConflict()禁用指向jQuery的$变量。如果它被禁用,你的代码仍然可以使用$而不是中断,如果你这样做:

(function($) { ...code...})(jQuery);

答案 6 :(得分:3)

  1. 为避免在同一窗口中与其他方法/库发生冲突,
  2. 避免全局范围,使其成为本地范围,
  3. 为了更快地进行调试(本地范围),
  4. JavaScript只有函数作用域,因此它也有助于编译代码。

答案 7 :(得分:1)

我们还应该使用' use strict'在范围函数中,以确保代码应在"严格模式"中执行。示例代码如下所示

(function() {
    'use strict';

    //Your code from here
})();