我最近一直在阅读大量的Javascript,并且我注意到整个文件在要导入的.js文件中包含如下内容。
(function() {
...
code
...
})();
这样做的原因是什么,而不是一组简单的构造函数?
答案 0 :(得分:760)
通常是命名空间(见后面)并控制成员函数和/或变量的可见性。把它想象成一个对象定义。 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()那样被多次调用。 / p>
巧妙的是,您还可以在内部定义内容并将其公开给外部世界(例如命名空间,以便您基本上可以创建自己的库/插件):
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之后工作。
答案 1 :(得分:75)
在最简单的形式中,此技术旨在将代码包装在函数范围中。
它有助于降低:
的机会 没有检测文档何时准备就绪 - 它不是某种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
声明)将是" private"并且只能在功能范围内访问(正如Vivin Paliath所说)。换句话说,这些变量在函数外部是不可见/可达的。 See live demo
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
总有一天,有人可能会想到"必须有办法避免命名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
下一步是思考"为什么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等服务器环境相比),全局范围由window
对象定义。因此我们可以window.myOtherFunction()
。
我的"良好做法"关于此主题的提示是在定义任何内容时始终使用var
:它是数字,对象还是函数,&即使在全球范围内。这使代码更加简单。
注意:
block scope
(更新:ES6中添加的块范围局部变量。)function scope
& global scope
(浏览器环境中的window
范围)详细了解Javascript Scopes
:
获得此IIFE
概念后,它会导致module pattern
,这通常是通过利用此IIFE模式完成的。玩得开心:))
答案 2 :(得分:26)
浏览器中的Javascript只有几个有效的范围:功能范围和全局范围。
如果变量不在函数范围内,则它在全局范围内。全局变量通常都很糟糕,因此这是一个将库的变量保存到自身的构造。
答案 3 :(得分:19)
这叫做封闭。它基本上密封了函数内部的代码,以便其他库不会干扰它。它类似于用编译语言创建命名空间。
实施例。假设我写道:
(function() {
var x = 2;
// do stuff with x
})();
现在其他库无法访问我创建的变量x
以在我的库中使用。
答案 4 :(得分:8)
您也可以在较大的表达式中将函数闭包用作数据,就像在确定浏览器对某些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;
})()
}
答案 5 :(得分:7)
除了将变量保持为局部之外,一个非常方便的用途是在使用全局变量编写库时,可以为其提供一个较短的变量名,以便在库中使用。它经常用于编写jQuery插件,因为jQuery允许你使用jQuery.noConflict()禁用指向jQuery的$变量。如果它被禁用,你的代码仍然可以使用$而不是中断,如果你这样做:
(function($) { ...code...})(jQuery);
答案 6 :(得分:3)
答案 7 :(得分:1)
我们还应该使用' use strict'在范围函数中,以确保代码应在"严格模式"中执行。示例代码如下所示
(function() {
'use strict';
//Your code from here
})();