将整个Javascript文件包装在匿名函数中的目的是什么,例如“(function(){ ... })()"?总之更多详情资源后续步骤

2022-08-29 22:40:35

我最近一直在阅读很多Javascript,我注意到整个文件被包装在要导入.js文件中。

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

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


答案 1

它通常是命名空间(见后面)并控制成员函数和/或变量的可见性。把它想象成一个对象定义。它的技术名称是立即调用的函数表达式 (IIFE)。jQuery插件通常是这样写的。

在Javascript中,你可以嵌套函数。因此,以下内容是合法的:

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

现在你可以调用 ,但 的可见性被限制在 的范围,这意味着它是私有的。它基本上遵循与Javascript中的变量相同的原理:outerFunction()innerFunction()outerFunction()outerFunction()

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() { ... })()()

(function() {
   var private_var;

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

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

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

编辑

我忘了提。在最后,你可以通过任何你想要的东西。例如,当您创建jQuery插件时,您可以传入或类似内容:()jQuery$

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

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

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

前面我描述了这些函数如何在启动时自动运行,但是如果它们自动运行,谁在传入参数?此技术假定所需的所有参数都已定义为全局变量。因此,如果jQuery尚未被定义为全局变量,则此示例将不起作用。正如您可能猜到的那样,jquery.js在初始化期间所做的一件事是定义一个“jQuery”全局变量,以及其更着名的“$”全局变量,这允许此代码在包含jQuery之后工作。


答案 2

总之

总结

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

它有助于减少以下几率:

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

它不检测文档何时准备就绪 - 它不是某种或document.onloadwindow.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

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

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',因为我们想要的就是立即执行它。

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

  • 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

观看现场演示

迭代 2

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

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

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

观看现场演示

它不起作用,因为“函数声明不可调用”。

诀窍是,通过删除,我们将函数表达式转换为函数声明。有关此内容的更多详细信息,请参阅“参考资料”中的链接。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

观看现场演示

基本上,如果一个未在其当前作用域中声明的变量被赋值,则“在找到该变量或命中全局作用域(此时它将创建它)之前,将查找作用域链”。

在浏览器环境中(与 nodejs 等服务器环境相比),全局作用域由对象定义。因此,我们可以做到.windowwindow.myOtherFunction()

关于这个主题,我的“良好实践”技巧是在定义任何东西时始终使用var:无论是数字,对象还是函数,甚至在全局范围内也是如此。这使得代码更简单。

注意:

  • javascript没有(更新:在ES6中添加的块范围局部变量。block scope
  • javascript 只有 & ( 在浏览器环境中的作用域)function scopeglobal scopewindow

阅读更多关于 :Javascript Scopes


资源


后续步骤

一旦你得到了这个概念,它就会导致 ,这通常是通过利用这个IIFE模式来完成的。玩得开心:)IIFEmodule pattern