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

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

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


当前回答

它被称为OOP中的封装。

其他回答

它通常用于命名空间(参见稍后)和控制成员函数和/或变量的可见性。把它想象成一个对象定义。它的专业名称是立即调用函数表达式(IIFE)。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(),但不能调用localFunction1或localFunction2。

当你写(function(){…})(),是您正在使第一组括号内的代码成为一个函数字面量(意味着整个“对象”实际上是一个函数)。在此之后,您将自调用刚才定义的函数(final())。正如我之前提到的,这样做的主要好处是,你可以拥有私有方法/函数和属性:

(function() {
   var private_var;

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

在第一个示例中,您将显式地通过名称调用globalFunction来运行它。也就是说,您只需执行globalFunction()来运行它。但在上面的例子中,你不只是定义一个函数;您可以一次性定义和调用它。这意味着当你的JavaScript文件被加载时,它会立即被执行。当然,你可以这样做:

function globalFunction() {
    // code
}
globalFunction();

这两种行为基本相同,只有一个显著的不同:使用IIFE时避免了对全局作用域的污染(因此,这也意味着您不能多次调用该函数,因为它没有名称,但由于该函数只意味着执行一次,因此这确实不是问题)。

使用iife的好处是,你也可以在内部定义东西,只向外部公开你想要的部分(一个命名空间的例子,这样你就可以创建自己的库/插件):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

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

现在你可以调用myPlugin.public_function1(),但是你不能访问private_function()!这和类定义很相似。为了更好地理解这一点,我推荐以下链接进行进一步阅读:

对Javascript进行命名空间 Javascript中的私有成员(by Douglas Crockford)

EDIT

我忘了说了。在final()中,你可以在里面传递任何你想要的东西。例如,当你创建jQuery插件时,你像这样传入jQuery或$:

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

因此,您在这里要做的是定义一个接受一个参数的函数(称为jQ,一个局部变量,只有该函数知道)。然后自调用函数并传入一个参数(也称为jQuery,但这个参数来自外部,是对实际jQuery本身的引用)。这样做没有迫切的需要,但有一些好处:

您可以重新定义全局参数,并为其指定一个在局部范围内有意义的名称。 这有一点性能优势,因为在局部作用域中查找内容要快一些,而不必沿着作用域链进入全局作用域。 压缩(缩小)有好处。

Earlier I described how these functions run automatically at startup, but if they run automatically who is passing in the arguments? This technique assumes that all the parameters you need are already defined as global variables. So if jQuery wasn't already defined as a global variable this example would not work. As you might guess, one things jquery.js does during its initialization is define a 'jQuery' global variable, as well as its more famous '$' global variable, which allows this code to work after jQuery has been included.

浏览器中的Javascript只有两个有效作用域:函数作用域和全局作用域。

如果一个变量不在函数作用域中,它就是在全局作用域中。全局变量通常是不好的,所以这是一个构造,以保持库的变量本身。

提供一个接受的答案的例子,从https://requirejs.org/docs/whyamd.html:

(function () {
    var $ = this.jQuery;

    this.myExample = function () {};
}());

代码演示了我们可以:

在作用域中使用全局变量 导出函数、变量等。通过绑定this,也就是浏览器的窗口对象。

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

的例子。假设我这样写:

(function() {

    var x = 2;

    // do stuff with x

})();

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

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