我一直在努力理解以下代码:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
这里为add
分配了匿名自我调用函数的返回值 - 即函数function() { return counter += 1 }
。现在第一次调用add()
时,它会按预期返回1
。但第二次调用add()
时,它会返回2
。
我的问题是,因为counter
是在函数内部定义的,所以每次函数完成执行时counter
都不应该死掉?也就是说,在显示第一次调用add()
1之后。现在我们已经不在那个函数中了,所以不应该counter
忘记它以前的值,并且像automatic
变量一样从堆栈中销毁?
答案 0 :(得分:7)
javascript中自调用函数内变量的生命周期是什么
与任何其他类型的JavaScript函数中的变量相同:它们只要可以被引用就存在,这有时意味着包含它们的函数返回的时间很长。
在IIFE返回后,您的counter
变量继续存在,因为它创建并返回(return function () {return counter += 1;}
)的函数是变量上的闭包。只要该函数存在,变量就会存在。
更专业地说:调用一个函数为该调用创建一个名为执行上下文的东西,它有一个变量环境对象。在调用期间创建的任何函数都会接收对该外部变量环境对象的引用;与所有对象一样,该对象只要存在对它的引用就存在,因此这些函数使对象保持活动状态。变量实际上是变量环境对象的属性,因此只要某些东西引用了它们所依赖的对象,它们就会存在。 (这是非常简化的形式。)虽然理论上保留了整个变量环境对象,但实际上如果优化效果不可观察,JavaScript引擎可以自由优化,因此实际上并非实际的变量闭包使用可能(或可能不会)释放,具体取决于引擎和函数中的代码。
你的IIFE只能被调用一次,因此只能有一个counter
,但创建一个闭包被多次调用的函数很常见,在这种情况下你有多个变量对象,以及关闭的变量的多个副本。
示例:
function helloBuilder(name) {
var counter = 0;
return function() {
++counter;
display("Hi there, " + name + ", this is greeting #" + counter);
};
}
var helloFred = helloBuilder("Fred");
var helloMaria = helloBuilder("Maria");
helloFred(); // "Hi there, Fred, this is greeting #1"
helloFred(); // "Hi there, Fred, this is greeting #2"
helloMaria(); // "Hi there, Maria, this is greeting #1"
helloMaria(); // "Hi there, Maria, this is greeting #2"
helloFred(); // "Hi there, Fred, this is greeting #3"
function display(msg) {
var p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}

在上文中,helloBuilder
返回的函数将关闭其name
参数及其counter
变量。 (因为参数也存储在执行上下文的变量对象中。)因此我们可以看到,在调用它两次后,有两个变量对象,每个变量对象都有自己的name
和counter
,我们要求helloBuilder
创建的每个函数引用的一个。