我最近读了很多Javascript,我注意到整个文件在要导入的.js文件中是像下面这样包装的。

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

为什么要这样做而不是简单的构造函数集呢?


当前回答

这叫做闭包。它基本上将代码密封在函数内部,这样其他库就不会干扰它。这类似于在编译语言中创建名称空间。

的例子。假设我这样写:

(function() {

    var x = 2;

    // do stuff with x

})();

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

其他回答

我们还应该在作用域函数中使用“use strict”,以确保代码应该在“严格模式”下执行。示例代码如下所示

(function() {
    'use strict';

    //Your code from here
})();

简而言之

总结

在最简单的形式中,这种技术旨在将代码包装在函数作用域中。

它有助于减少发生以下情况的机会:

与其他应用程序/库冲突 污染范围大(很可能是全球范围)

它不检测文档何时准备好—它不是某种文档。Onload或window.onload

它通常被称为立即调用函数表达式(IIFE)或自执行匿名函数。

代码解释

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声明)都将是“私有的”,并且只能在函数作用域内访问(正如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

见现场演示。


迭代1

有一天,有人可能会想“必须有一种方法来避免命名‘myMainFunction’,因为我们只想立即执行它。”

如果你回到最基本的问题,你会发现:

表达式:求值的东西。即3 + 11 / x 语句:行代码做一些事情,但它没有求值。即如果(){}

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

 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

见现场演示。

迭代2

下一步是思考“如果我们甚至不使用var myMainFunction =为什么!?”

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

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

见现场演示。

它不能工作,因为“函数声明是不可调用的”。

诀窍在于,通过删除var myMainFunction =,我们将函数表达式转换为函数声明。有关这方面的更多详细信息,请参阅“参考资料”中的链接。

下一个问题是“为什么我不能把它作为一个函数表达式,而不是var myMainFunction =?”

答案是“你可以”,实际上有很多方法可以做到这一点:添加一个+,一个!,一个-,或者用一对圆括号括起来(这是现在的惯例),我相信还有更多。为例:

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

or

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

or

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

感叹号在函数前有什么作用? JavaScript函数名前的加号

因此,一旦相关的修改被添加到我们曾经的“替代代码”中,我们就会返回到与“代码解释”示例中使用的完全相同的代码

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

阅读更多关于表达式和语句的内容:

developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions # Function_constructor_vs._function_declaration_vs._function_expression Javascript:语句和表达式之间的区别? 表达式与语句


揭秘范围

人们可能会想知道的一件事是“如果你没有在函数中正确地定义变量——即做一个简单的赋值,会发生什么?”

(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

见现场演示。

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

当在浏览器环境中(相对于像nodejs这样的服务器环境),全局作用域由window对象定义。因此我们可以执行window.myOtherFunction()。

关于这个主题,我的“良好实践”建议是在定义任何东西时始终使用var:无论是数字、对象还是函数,即使是在全局作用域中。这使得代码更加简单。

注意:

javascript没有块作用域(更新:块作用域局部变量在ES6中添加。) Javascript只有函数作用域和全局作用域(浏览器环境中的窗口作用域)

阅读更多关于Javascript作用域的内容:

var关键字的目的是什么,什么时候使用它(或省略它)? JavaScript中变量的作用域是什么?


资源

youtu.be / i_qE1iAmjFg吗?t=2m15s -保罗·爱尔兰在2:15分展示IIFE,一定要看! developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions 书:Javascript,好的部分-强烈推荐 youtu.be / i_qE1iAmjFg吗?t=4m36s - Paul Irish在4:36给出了模块模式


下一个步骤

一旦您获得了这个IIFE概念,就会得到模块模式,这通常是通过利用这个IIFE模式来完成的。玩得开心!

你也可以在更大的表达式中使用函数闭包作为数据,就像在这个方法中确定浏览器对某些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;
     })()
    }

为了避免与同一窗口中的其他方法/库冲突, 避免全球范围,让它成为局部范围, 为了加快调试(本地范围), JavaScript只有函数作用域,所以它也有助于代码的编译。

除了保持变量在本地之外,一个非常方便的用法是在使用全局变量编写库时,可以给它一个更短的变量名,以便在库中使用。它经常用于编写jQuery插件,因为jQuery允许您使用jQuery. noconflict()禁用指向jQuery的$变量。如果它被禁用,你的代码仍然可以使用$,如果你只是这样做:

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