该构造被称为自执行匿名函数,实际上这不是一个很好的名字,这就是发生的事情(以及为什么这个名字不是一个好名字)。这个:
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 的 last 值(在这种情况下,它可能是 2,这将导致麻烦)。我们可能希望在循环中创建的每个函数在创建时锁定 i 的值。这就是我们的自执行匿名函数的用武之地,这里有一种类似但可能更容易理解的重写循环的方法:
for ( var i = 0; i < items.length; i++ ) {
(function(index){
obj[ "on" + item ] = function() {
alert( "Thanks for your " + items[index] );
};
})(i);
}
因为我们在每次迭代中调用我们的匿名函数,所以我们传入的参数被锁定为传入时的值,因此我们在循环中创建的所有函数都将按预期工作。
你去吧,使用自执行匿名函数构造的两个很好的理由,以及它为什么首先真正起作用。