什么是过期闭包
"过期闭包"(stale closure)是指闭包(Closure)中的变量引用的是旧的、已经过期的值的情况。
闭包是指在一个函数内部创建的函数,并且该内部函数引用了其外部函数的变量。闭包可以让内部函数访问和操作外部函数中的变量,即使外部函数已经执行完毕,闭包仍然可以访问和保持对这些变量的引用。
当闭包中引用的外部变量发生变化时,闭包通常会跟随变化,并且可以正确地访问到最新的值。但在某些情况下,当闭包被创建时,它引用的外部变量的值是一个旧的值,而不是最新的值,这就是过期闭包。
过期闭包的常见情况发生在使用循环变量(例如 `for` 循环中的计数器)创建闭包的时候。由于 JavaScript 中的变量作用域和变量共享机制,循环变量的值在每次迭代中都会被修改,但闭包在循环结束后才被调用,此时闭包中引用的循环变量值已经是循环结束时的最终值,而不是每次迭代的值。
以下是一个经典的过期闭包示例:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // 输出 5,而不是预期的 0、1、2、3、4
}, 1000);
}
在上述示例中,`setTimeout` 函数会在循环结束后,延迟一秒钟执行传入的函数。当这个函数被执行时,它引用的 `i` 变量已经是循环结束时的最终值 5,而不是每次迭代的值。因此,输出的结果会是 5,而不是预期的 0、1、2、3、4。
为了避免过期闭包的问题,可以通过创建一个新的作用域来捕获每次迭代的值,例如使用 IIFE(立即调用函数表达式)或 ES6 中的块级作用域(使用 `let` 关键字)来解决该问题。这样可以确保每个闭包都引用正确的值。
for (var i = 0; i < 5; i++) {
(function(index) {
setTimeout(function() {
console.log(index); // 输出 0、1、2、3、4
}, 1000);
})(i);
}
在上述修改后的示例中,通过使用立即调用函数表达式(IIFE)创建一个新的作用域,并将每次迭代的值作为参数传递给该函数,确保每个闭包引用的是正确的值。这样就可以按预期输出 0、1、2、3、4。