将整个 Javascript 文件包装在像“(function(){ … })()”这样的匿名函数中的目的是什么?
-
19-09-2019 - |
题
我最近读了很多 Javascript,我注意到整个文件在要导入的 .js 文件中像下面这样包装。
(function() {
...
code
...
})();
这样做的原因是什么而不是一组简单的构造函数?
解决方案
它通常是命名空间(见下文)并控制成员函数和/或变量的可见性。将其视为对象定义。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() { ... code ... })()
, ,您是否正在函数文字中编写代码(意味着整个“对象”实际上是一个函数)。之后,您将自调用该函数(最终的 ()
)。因此,正如我之前提到的,它的主要优点是您可以拥有私有方法/函数和属性:
(function() {
var private_var;
function private_function() {
//code
}
})()
在第一个示例中,globalFunction() 是公共函数,可以调用它来访问公共功能,但在上面的示例中如何调用它?这里的自调用函数使代码在启动时自动运行。就像你可以添加 initMyStuff(); 一样到任何 .js 文件的顶部,它将作为全局范围的一部分自动运行,这个自调用函数也将自动运行,尽管因为它是一个未命名的函数,所以不能像 initMyStuff() 那样被多次调用。
巧妙的是,您还可以在内部定义内容并将其暴露给外部世界(命名空间的示例,以便您基本上可以创建自己的库/插件):
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)
因此,您在这里所做的就是定义一个接受一个参数(称为 jQ
, ,一个局部变量,并且已知 仅有的 到该功能)。然后你自调用该函数并传入一个参数(也称为 jQuery
, , 但 这 一个来自外部世界,一个是对实际 jQuery 本身的引用)。没有迫切需要这样做,但有一些优点:
- 您可以重新定义全局参数并为其指定一个在本地范围内有意义的名称。
- 有一点性能优势,因为在本地作用域中查找内容比必须沿着作用域链进入全局作用域更快。
- 压缩(缩小)有好处。
前面我描述了这些函数如何在启动时自动运行,但如果它们自动运行,谁来传递参数呢?该技术假设所有参数都定义为全局变量。因此,如果 jQuery 未定义为全局变量,则此示例将无法工作,并且无法以任何其他方式调用,因为我们的示例是匿名函数。正如您可能猜到的,jquery.js 在初始化期间所做的一件事是定义一个“jQuery”全局变量,以及更著名的“$”全局变量,这允许此代码在包含 jquery.js 后工作。
其他提示
简而言之
概括
以最简单的形式,该技术旨在将代码包装在 功能范围.
它有助于减少以下情况的机会:
- 与其他应用程序/库发生冲突
- 污染上级(全球最有可能)范围
它 才不是 检测文档何时准备好 - 它不是某种 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
在上面的示例中,函数中定义的任何变量(即声明使用 var
) 将是“私有的”并且只能在函数范围内访问(正如 Vivin Paliath 所说)。换句话说,这些变量在函数外部不可见/不可访问。 查看现场演示.
Javascript 有函数作用域。“函数中定义的参数和变量在函数之外不可见,并且在函数中,函数内的任何地方都可以在函数中可见。” (来自“ JavaScript:好的部分”)。
更多细节
替代代码
最后,之前贴的代码也可以这样做:
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
:评估某个值的东西。IE。3+11/x
statement
:代码行做某事但它确实做了 不是 评估为一个值。IE。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 =
?
答案是“你可以”,实际上有很多方法可以做到这一点:添加一个 +
, , A !
, , A -
, ,或者可能用一对括号括起来(正如现在按照惯例所做的那样),我相信还有更多。例如:
(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
:
- 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 确实 不是 有
block scope
(更新:添加的块作用域局部变量 ES6.) - JavaScript 只有
function scope
&global scope
(window
浏览器环境中的范围)
阅读更多关于 Javascript Scopes
:
资源
- youtu.be/i_qE1iAmjFg?t=2m15s - Paul Irish 将在 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
的概念,它导致 module pattern
, ,这通常是通过利用这种 IIFE 模式来完成的。玩得开心 :)
的JavaScript在浏览器中唯一真正有几个有效范围:功能范围和全局范围
如果一个变量不是在功能范围内,它是在全局范围。和全局变量一般是不好的,所以这是保持一个图书馆的变量本身的构造。
这就是所谓的封闭件。它基本上密封功能中的代码,以便其他库不干扰它。它类似于在创建编译语言命名空间。
实施例。假设我写:
(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;
})()
}
除了保持局部变量,一个非常方便的使用是写入使用全局变量库时,你可以给它一个更短的变量名库中使用。它通常以书面的jQuery插件使用,因为jQuery的可以让你禁用$变量指向jQuery的,使用jQuery.noConflict()。如果它被禁用,你的代码仍然可以使用$,而不是打破,如果你只是做:
(function($) { ...code...})(jQuery);
- 为了避免与同一窗口中的其他方法/库发生冲突,
- 避免全局范围,使其成为局部范围,
- 为了使调试更快(本地范围),
- JavaScript 只有函数作用域,因此它也有助于代码的编译。
我们也应该使用“使用严格”的范围内的功能,以确保代码应该本着“严谨模式”被执行。下面示出的代码示例
(function() {
'use strict';
//Your code from here
})();