我一直在javascript源码中看到这一点,但我从未真正发现使用此构造的真正原因。为什么需要这个?
(function() {
//stuff
})();
为什么这样写的?为什么不单独使用stuff
而不是函数?
答案 0 :(得分:26)
这用于创建一个具有私有功能和非全局可见变量的函数闭包。
请考虑以下代码:
(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 running code,如上面代码中的注释所示。
你已经知道了这个
创建一个函数:
function() { ... }
并立即执行:
(func)();
此功能可能会或可能不会接受其他参数。
jQuery插件通常以这种方式定义,通过使用插件操作的一个参数定义一个函数:
(function(paramName){ ... })(jQuery);
但主要思想仍然是相同的:使用不能直接在其外部使用的私有定义来定义函数闭包。
答案 1 :(得分:13)
这个结构被称为一个自动执行的匿名函数,它实际上并不是一个非常好的名称,这里发生了什么(以及为什么名称不是很好)。这样:
function abc() {
//stuff
}
定义一个名为abc
的函数,如果我们想要一个匿名函数(这是javascript中非常常见的模式),它将是以下内容:
function() {
//stuff
}
但是,如果你有这个,你需要将它与一个变量相关联,这样你就可以调用它(这会让它变得不那么匿名),或者你需要立即执行它。我们可以尝试直接执行它:
function() {
//stuff
}();
但这不会起作用,因为它会给你一个语法错误。您收到语法错误的原因如下。当您创建一个具有名称的函数(例如上面的 abc )时,该名称将成为函数 expression 的引用,然后您可以通过put()来执行该表达式名称例如: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);
}
因为我们在每次迭代时调用我们的匿名函数,所以我们传入的参数被锁定到它传入时的值,因此我们在循环中创建的所有函数都将按预期工作。
你有两个很好的理由使用自动执行的匿名函数结构以及为什么它实际上起作用。
答案 2 :(得分:3)
它用于定义匿名函数然后调用它。我没有尝试,但我最好的猜测为什么在块周围有parens是因为JavaScript需要他们理解函数调用。
如果要在适当位置定义一次性功能然后立即调用它,这将非常有用。使用匿名函数和编写代码之间的区别在于范围。当函数结束时,匿名函数中的所有变量都将超出范围(当然,除非说明变量)。这可以用来保持全局或封闭的命名空间干净,长期使用更少的内存,或获得一些“隐私”。
答案 3 :(得分:1)
它是“匿名自执行函数”或“立即调用函数表达式”。 Ben Alman的好解释here。
我在创建命名空间时使用模式
var APP = {};
(function(context){
})(APP);
答案 4 :(得分:1)
当你想要一个闭包时,这样的构造很有用 - 一个构造有助于为外部无法访问的变量创建一个私有的“空间”。请参阅本章“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()
方法之外无法访问它。
答案 5 :(得分:1)
我不确定这个问题是否已经得到解答,如果我只是重复一些内容,请道歉。
在JavaScript中,仅限功能引入新的范围。通过将代码包装在立即函数中,您定义的所有变量仅存在于此范围或较低范围内,但不在全局范围内。
因此,这是不污染全球范围的好方法。
应该只有几个全局变量。请记住,每个全局都是window
对象的属性,默认情况下它已经具有很多属性。引入新范围还可以避免与window
对象的默认属性发生冲突。