我一直在 javascript 源代码中看到这一点,但我从未真正找出使用此构造的真正原因。为什么需要这个?
(function() {
//stuff
})();
为什么要这样写?为什么不单独使用stuff
而不是在函数中使用?
编辑:我知道这是定义一个匿名函数然后调用它,但为什么呢?
我一直在 javascript 源代码中看到这一点,但我从未真正找出使用此构造的真正原因。为什么需要这个?
(function() {
//stuff
})();
为什么要这样写?为什么不单独使用stuff
而不是在函数中使用?
编辑:我知道这是定义一个匿名函数然后调用它,但为什么呢?
这用于创建具有私有功能和全局不可见变量的函数闭包。
考虑以下代码:
(function(){
var test = true;
})();
变量test
在其他任何地方都不可见,但在定义它的函数闭包内。
函数闭包使各种脚本可以不相互干扰,即使它们定义了类似命名的变量或私有函数。这些私有信息仅在闭包内部可见和可访问,而在其外部无法访问。
检查此代码并阅读注释:
// public part
var publicVar = 111;
var publicFunc = function(value) { alert(value); };
var publicObject = {
// no functions whatsoever
};
// closure part
(function(pubObj){
// private variables and functions
var closureVar = 222;
var closureFunc = function(value){
// call public func
publicFunc(value);
// alert private variable
alert(closureVar);
};
// add function to public object that accesses private functionality
pubObj.alertValues = closureFunc;
// mind the missing "var" which makes it a public variable
anotherPublic = 333;
})(publicObject);
// alert 111 & alert 222
publicObject.alertValues(publicVar);
// try to access varaibles
alert(publicVar); // alert 111
alert(anotherPublic); // alert 333
alert(typeof(closureVar)); // alert "undefined"
这是一个JSFiddle 运行代码,它显示由上层代码中的注释指示的数据。
正如你已经知道的
创建一个函数:
function() { ... }
并立即执行它:
(func)();
这个函数可能接受也可能不接受额外的参数。
jQuery 插件通常是这样定义的,通过定义一个带有插件操作的参数的函数:
(function(paramName){ ... })(jQuery);
但主要思想仍然相同:定义一个带有不能直接在其外部使用的私有定义的函数闭包。
该构造被称为自执行匿名函数,实际上这不是一个很好的名称,这是发生的情况(以及为什么名称不是一个好名称)。这:
function abc() {
//stuff
}
定义一个名为 的函数abc
,如果我们想要一个匿名函数(这是 javascript 中非常常见的模式),它会是这样的:
function() {
//stuff
}
但是,如果你有这个,你要么需要将它与一个变量相关联,以便你可以调用它(这将使它不那么匿名),或者你需要立即执行它。我们可以尝试通过这样做立即执行它:
function() {
//stuff
}();
但这不会起作用,因为它会给你一个语法错误。出现语法错误的原因如下。当您使用名称(例如上面的abc)创建函数时,该名称将成为对函数表达式的引用,然后您可以通过在名称后放置 () 来执行该表达式,例如:abc()
。声明函数的行为不会创建表达式,函数声明实际上是语句而不是表达式。本质上,表达式是可执行的,而语句则不是(您可能已经猜到了)。所以为了执行一个匿名函数,你需要告诉解析器它是一个表达式而不是一个语句。一这样做的方法(不是唯一的方法,但它已成为惯例)是将您的匿名函数包装在一组中()
,这样您就可以得到您的构造:
(function() {
//stuff
})();
一个立即执行的匿名函数(您可以看到构造的名称有点偏离,因为它实际上并不是一个执行自身的匿名函数,而是一个立即执行的匿名函数)。
好的,那么为什么这一切都很有用,一个原因是它可以让您阻止代码污染全局命名空间。因为 javascript 中的函数有自己的作用域,函数内的任何变量都不是全局可见的,所以如果我们能以某种方式将所有代码写在一个函数中,全局作用域将是安全的,我们的自执行匿名函数允许我们这样做. 让我借用 John Resig 的旧书中的一个例子:
// Create a new anonymous function, to use as a wrapper
(function(){
// The variable that would, normally, be global
var msg = "Thanks for visiting!";
// Binding a new function to a global object
window.onunload = function(){
// Which uses the 'hidden' variable
alert( msg );
};
// Close off the anonymous function and execute it
})();
我们所有的变量和函数都写在我们的自执行匿名函数中,我们的代码首先被执行,因为它在一个自执行匿名函数中。由于 javascript 允许闭包,即本质上允许函数访问在外部函数中定义的变量,我们几乎可以在自执行匿名函数中编写我们喜欢的任何代码,并且一切仍会按预期工作。
但是等等还有更多:)。这种构造使我们能够解决在 javascript 中使用闭包时有时会出现的问题。我将再次让 John Resig 解释一下,我引用:
请记住,闭包允许您引用父函数中存在的变量。但是,它不提供变量在创建时的值;它提供父函数中变量的最后一个值。您将看到这种情况发生的最常见问题是在 for 循环期间。有一个变量被用作迭代器(例如,i)。在 for 循环内部,正在创建使用闭包再次引用迭代器的新函数。问题是当新的闭包函数被调用时,它们将引用迭代器的最后一个值(即数组中的最后一个位置),而不是您期望的值。清单 2-16 显示了一个使用匿名函数来诱导作用域的示例,以创建一个可以预期关闭的实例。
// An element with an ID of main
var obj = document.getElementById("main");
// An array of items to bind to
var items = [ "click", "keypress" ];
// Iterate through each of the items
for ( var i = 0; i < items.length; i++ ) {
// Use a self-executed anonymous function to induce scope
(function(){
// Remember the value within this scope
var item = items[i];
// Bind a function to the element
obj[ "on" + item ] = function() {
// item refers to a parent variable that has been successfully
// scoped within the context of this for loop
alert( "Thanks for your " + item );
};
})();
}
基本上所有这一切的意思是,人们经常像这样编写朴素的 javascript 代码(这是上面循环的朴素版本):
for ( var i = 0; i < items.length; i++ ) {
var item = items[i];
// Bind a function to the elment
obj[ "on" + item ] = function() {
alert( "Thanks for your " + items[i] );
};
}
我们在循环中创建的函数是闭包,但不幸的是,它们会锁定封闭作用域中的最后一个值i
(在这种情况下,它可能是 2,这会导致麻烦)。我们可能希望我们在循环中创建的每个函数在我们创建它时锁定 i 的值。这就是我们的自执行匿名函数的用武之地,这是一种类似但可能更容易理解的重写该循环的方法:
for ( var i = 0; i < items.length; i++ ) {
(function(index){
obj[ "on" + item ] = function() {
alert( "Thanks for your " + items[index] );
};
})(i);
}
因为我们在每次迭代时调用我们的匿名函数,所以我们传入的参数被锁定为传入时的值,因此我们在循环中创建的所有函数都将按预期工作。
好了,使用自执行匿名函数构造的两个很好的理由以及为什么它首先真正起作用。
它用于定义一个匿名函数,然后调用它。我还没有尝试过,但我对为什么在块周围有括号的最佳猜测是因为 JavaScript 需要它们来理解函数调用。
如果您想就地定义一次性函数然后立即调用它,这会很有用。使用匿名函数和只写出代码的区别在于作用域。当函数结束时,匿名函数中的所有变量都将超出范围(当然,除非另有说明)。这可用于保持全局或封闭命名空间清洁,长期使用较少的内存,或获得一些“隐私”。
它是一个“匿名自执行函数”或“立即调用的函数表达式”。Ben Alman在这里给出了很好的解释。
我在创建命名空间时使用该模式
var APP = {};
(function(context){
})(APP);
当您想创建一个闭包时,这样的构造很有用 - 构造有助于为外部无法访问的变量创建一个私有的“房间”。在“JavaScript:好的部分”一书的这一章中查看更多信息:http : //books.google.com/books?id=PXa2bby0oQ0C&pg=PA37&lpg=PA37&dq=crockford+closure+called+immediately&source=bl&ots=HIlku8x4jL&sig=-T- T0jTmf7_p_6twzaCq5_5aj3A&hl=lv&ei=lSa5TaXeDMyRswa874nrAw&sa=X&oi=book_result&ct=result&resnum=1&ved=0CBUQ6AEwAA#v=onepage&q&f=false
在第 38 页顶部显示的示例中,您会看到变量“status”隐藏在闭包中,除了调用get_status()
方法外无法访问。