What is the purpose of wrapping whole Javascript files in anonymous functions like “(function(){ … })()”?
最近我读了很多javascript,我注意到整个文件都像下面这样包装在要导入的.js文件中。
1 2 3 4 5 | (function() { ... code ... })(); |
为什么要这样做而不是一组简单的构造函数?
它通常用于命名(见下文)和控制成员函数和/或变量的可见性。把它想象成一个对象定义。jquery插件通常是这样编写的。
在JavaScript中,可以嵌套函数。因此,以下是合法的:
1 2 3 4 5 | function outerFunction() { function innerFunction() { // code } } |
现在您可以调用
1 2 3 4 5 | var globalVariable; function someFunction() { var localVariable; } |
相应地:
1 2 3 4 5 6 7 8 9 10 | function globalFunction() { var localFunction1 = function() { //I'm anonymous! But localFunction1 is a reference to me! }; function localFunction2() { //I'm named! } } |
在上述情况下,您可以从任何地方调用
当您编写
1 2 3 4 5 6 7 | (function() { var private_var; function private_function() { //code } })() |
在第一个示例中,globalfunction()是可以调用以访问公共功能的公共函数,但在上面的示例中,如何调用它?在这里,自调用函数使代码在启动时自动运行。正如您可以将initmystuff();添加到任何.js文件的顶部,它将作为全局范围的一部分自动运行一样,这个自调用函数也将自动运行,尽管它是一个未命名的函数,不能像initmystuff()那样多次调用它。
简洁的一点是,您还可以定义内部的内容并将其公开给外部世界,因此(名称间距的一个示例,这样您基本上可以创建自己的库/插件):
1 2 3 4 5 6 7 8 9 10 11 12 13 | var myPlugin = (function() { var private_var; function private_function() { } return { public_function1: function() { }, public_function2: function() { } } })() |
现在您可以调用
- 名称间距您的javascript
- javascript中的私有成员(Douglas Crockford)
编辑
我忘了提。在最后一个
1 | (function(jQ) { ... code ... })(jQuery) |
因此,您在这里所做的是定义一个接受一个参数的函数(称为
- 您可以重新定义一个全局参数,并给它一个在本地范围内有意义的名称。
- 有一点性能优势,因为在本地范围内查找内容更快,而不必沿着范围链进入全局范围。
- 压缩(缩小)有好处。
前面我描述了这些函数如何在启动时自动运行,但是如果它们自动运行,那么谁来传递参数呢?此技术假定所有参数都定义为全局变量。因此,如果jquery没有定义为全局变量,那么这个示例将不起作用,并且不能以任何其他方式调用,因为我们的示例是一个匿名函数。正如您可能猜测的那样,jquery.js在初始化期间所做的一件事是定义一个"jquery"全局变量,以及更著名的"$"全局变量,它允许在包含jquery.js之后使用此代码。
简而言之总结
这种技术的最简单形式是将代码包装在函数范围内。好的。
它有助于减少发生以下情况的机会:好的。
- 与其他应用程序/库冲突
- 污染优势(全球最有可能)范围
它不检测文档准备就绪的时间——它不是某种类型的
它通常被称为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
在上面的示例中,函数中定义的任何变量(即使用
javascript具有函数范围。"函数中定义的参数和变量在函数外部不可见,函数中定义的变量在函数内部的任何位置都可见。好的。更多细节替代代码
最后,以前发布的代码也可以按如下方式执行:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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(){} 。
同样,函数表达式的计算结果也是一个值。还有一个后果(我想?)可以立即调用它们:好的。
1 | var italianSayinSomething = function(){ console.log('mamamia!'); }(); |
所以我们更复杂的例子是:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
下一步是思考"如果我们甚至不使用它,为什么要使用
答案很简单:请尝试删除此项,如下所示:好的。
1 | function(){ console.log('mamamia!'); }(); |
看实况演示。好的。
因为"函数声明是不可调用的"。好的。
诀窍是,通过删除
下一个问题是"为什么我不能把它作为一个函数表达式保存在除
答案是"你可以",实际上有很多方法可以做到这一点:添加一个
1 | (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console. |
或好的。
1 | +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console |
或好的。
1 | -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console |
- 感叹号在函数之前做什么?
- 函数名前的javascript加号
因此,一旦将相关的修改添加到曾经的"可选代码"中,我们将返回与"代码解释"示例中使用的代码完全相同的代码。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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_和_操作符
- developer.mozilla.org/en-us/docs/web/javascript/reference/functions function构造器vs.函数声明vs.函数表达式
- javascript:语句和表达式之间的区别?
- 表达式与语句
解除定义范围
有一件事我们可能会想,"当你没有在函数内部正确地定义变量时会发生什么——也就是说,做一个简单的赋值?"好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | (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等服务器环境相比)中,全局范围由
关于这个主题,我的"良好实践"提示是在定义任何东西时总是使用
注:好的。
- javascript没有
block scope (更新:在es6中添加了块作用域局部变量。) - javascript只有
function scope 和global scope (浏览器环境中的window 范围)
了解更多有关
- var关键字的用途是什么?何时使用它(或忽略它)?
- javascript中的变量范围是什么?
资源
- 你是我吗?T=2分15秒-保罗·爱尔兰在2点15分呈现生命,一定要看这个!
- developer.mozilla.org/en-us/docs/web/javascript/reference/functions
- 书:javascript,好的部分-强烈推荐
- 你是我吗?T=4m36s-Paul Irish在4:36呈现模块模式
下一步
一旦你得到了这个
浏览器中的javascript实际上只有两个有效的作用域:函数作用域和全局作用域。
如果变量不在函数范围内,则它在全局范围内。全局变量通常是不好的,所以这是一个将库变量保持为自身的构造。
这叫做结束。它基本上将代码密封在函数内部,这样其他库就不会干扰它。它类似于用编译语言创建名称空间。
例子。假设我写:
1 2 3 4 5 6 7 | (function() { var x = 2; // do stuff with x })(); |
现在,其他库无法访问我创建用于库中的变量
您也可以在更大的表达式中使用函数闭包作为数据,也可以在这种方法中确定对某些HTML5对象的浏览器支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; })() } |
除了保持变量的局部性,一个非常方便的用法是在使用全局变量编写库时,您可以给它一个较短的变量名,以便在库中使用。它通常用于编写jquery插件,因为jquery允许您使用jquery.noconflict()禁用指向jquery的$variable。如果它被禁用,您的代码仍然可以使用$而不是中断,如果您只是这样做:
1 | (function($) { ...code...})(jQuery); |
我们还应该在作用域函数中使用"use strict",以确保代码在"strict模式"下执行。下面显示的示例代码
1 2 3 4 5 | (function() { 'use strict'; //Your code from here })(); |