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

And*_*Kou 572 javascript scope coding-style iife

我最近一直在阅读很多Javascript,并且我注意到要导入的.js文件中的整个文件包含如下所示.

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

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

Viv*_*ath 777

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

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

function outerFunction() {
   function innerFunction() {
      // code
   }
}
Run Code Online (Sandbox Code Playgroud)

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

var globalVariable;

function someFunction() {
   var localVariable;
}
Run Code Online (Sandbox Code Playgroud)

与此相对应:

function globalFunction() {

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

   function localFunction2() {
      //I'm named!
   }
}
Run Code Online (Sandbox Code Playgroud)

在上面的场景中,你可以globalFunction()从任何地方打电话,但你不能打电话localFunction1localFunction2.

你在写作时所做的(function() { ... code ... })()是,你是在函数文字中创建代码(意味着整个"对象"实际上是一个函数).在那之后,你是自我调用函数(最后的()).所以我之前提到的主要优点是你可以拥有私有方法/函数和属性:

(function() {
   var private_var;

   function private_function() {
     //code
   }
})()
Run Code Online (Sandbox Code Playgroud)

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

整洁的事情是你也可以在里面定义东西并将它暴露给外面的世界(这是一个命名空间的例子,所以你基本上可以创建自己的库/插件):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()
Run Code Online (Sandbox Code Playgroud)

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

编辑

我忘了提.在那个决赛中(),你可以传递你想要的任何东西.例如,当你创建jQuery插件时,你传入jQuery或者$像这样:

(function(jQ) { ... code ... })(jQuery) 
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以重新定义全局参数,并为其指定在本地范围内有意义的名称.
  • 由于在本地范围内查找更快,而不必将范围链向全局范围内移动,因此具有轻微的性能优势.
  • 压缩(缩小)有好处.

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

  • 很棒的帖子.非常感谢. (33认同)
  • 非常酷,我理解命名空间很好,但我已经看到很多你的最后一个例子,无法弄清楚人们想要实现的目标.这真的很清楚. (14认同)
  • 很棒的帖子,我想更多关于如何将变量传递给自执行函数是有益的.自执行功能中的上下文是干净的 - 没有数据.您可以通过这样做来传递上下文`(function(context){.....})(this)`然后允许您将任何您喜欢的内容附加到父上下文中,从而暴露它. (4认同)
  • 我想添加一个前导和后面的分号';' 将使示例完整 - `;(function(jQ){... code ...})(jQuery);`这样,如果有人在他们的脚本中留下分号,它就不会打破你的分子,特别是如果你打算将您的脚本缩小并连接到其他脚本. (3认同)
  • 好帖子,我喜欢强调私有变量.我也喜欢模块模式/闭包(public_function1和public_function2)上的开放以及如何传递变量,即使稍微超出范围也是一个很好的介绍.我还添加了一个答案,这个集中在我想的是函数语句与函数表达式之间的语法和差异的根源以及我认为"只是一个约定"vs"实现这个结果的唯一方法". (3认同)
  • 好帖子!我想补充一点,在这个环境中通常在代码末尾有一个导出声明(有时它是这个匿名函数定义的唯一可执行部分).因此,大多数库以`this.myLib = myLib`,`define(myLib)`,`module.exports = myLib`或其组合的导出声明结束. (3认同)

Adr*_* Be 78

简而言之

摘要

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

它有助于降低以下几率:

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

,当文档准备好检测-这是不是某种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
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,函数中定义的任何变量(即声明的使用var)将是"私有的",并且只能在函数范围内访问(如Vivin Paliath所说).换句话说,这些变量在函数外部是不可见/可达的.查看现场演示.

Javascript具有函数范围."函数中定义的参数和变量在函数外部是不可见的,并且函数内任何位置定义的变量在函数内的任何地方都可见." (摘自"Javascript:The Good Parts").


更多细节

替代准则

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

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

查看现场演示.


迭代1

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

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

  • expression:评估价值的东西.即3+11/x
  • statement:代码行做某事但它没有评估为值.即if(){}

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

 var italianSayinSomething = function(){ console.log('mamamia!'); }();
Run Code Online (Sandbox Code Playgroud)

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

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

查看现场演示.

迭代2

下一步是"为什么var myMainFunction =我们甚至不使用它?" 的想法.

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

 function(){ console.log('mamamia!'); }();
Run Code Online (Sandbox Code Playgroud)

查看现场演示.

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

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

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

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

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

要么

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

要么

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

因此,一旦将相关修改添加到曾经的"替代代码"中,我们将返回与"代码说明"示例中使用的代码完全相同的代码

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

了解更多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
Run Code Online (Sandbox Code Playgroud)

查看现场演示.

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

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

我在这个主题上的"好习惯"提示是在定义任何东西时始终使用var:它是数字,对象还是函数,甚至是在全局范围内.这使代码更简单.

注意:

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

了解更多Javascript Scopes:


资源


下一步

一旦你得到这个IIFE概念,它就会导致这种情况module pattern,这通常是通过利用这种IIFE模式来实现的.玩得开心 :)


Gar*_*eth 26

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

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

  • @FranciscoPresencia"范围内的全局"不是一个有用的短语,因为这基本上就是"范围"的含义."全局"范围的重点在于它具体是*所有*其他范围可以访问的范围. (2认同)

Joe*_*oel 19

那被称为封闭.它基本上密封了函数内部的代码,以便其他库不会干扰它.它类似于在编译语言中创建命名空间.

例.假设我写道:

(function() {

    var x = 2;

    // do stuff with x

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

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

  • 小心你的术语.命名空间意味着可以通过寻址命名空间从外部访问变量(通常使用前缀).虽然这在Javascript中是可能的,但这不是在这里展示的 (7认同)

ken*_*bec 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;
     })()
    }
Run Code Online (Sandbox Code Playgroud)


小智 7

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

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


归档时间:

查看次数:

104523 次

最近记录:

7 年,11 月 前