在定时器中使用闭包的核心做法是将循环变量或外部状态包裹在回调函数中,从而保留每次迭代时的独立快照。具体而言,应使用 let 声明循环变量,或者将变量作为参数传递给立即执行函数表达式(iife),这样每个定时器回调都能访问到其创建时对应的变量值,避免所有定时器共享同一个最终变量值的问题。

为什么 var 会导致定时器输出错误结果
当使用 var 声明循环变量时,由于 var 具有函数作用域而非块级作用域,所有定时器回调都引用同一个变量实例。在定时器触发前,循环已经执行完毕,变量被更新为最终值,导致所有回调打印出相同的错误结果。这是 JavaScript 事件循环机制与变量作用域共同作用的结果。

for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100 * i);
}
上述代码的预期输出是 0, 1, 2,但实际输出是 2, 2, 2。这是因为 i 在所有回调执行时都已变为 2。
立即学习“Java免费学习笔记(深入)”;
如何使用 let 解决定时器中的变量捕获问题
使用 let 声明循环变量可以自动为每次迭代创建一个新的绑定,确保每个定时器回调捕获的是当前迭代的独立变量副本。这是现代 JavaScript 中最简洁且推荐的解决方案,无需额外的包装函数即可实现预期的行为。

for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100 * i);
}
上述代码的输出是 0, 1, 2,符合预期。每个 setTimeout 回调都捕获了各自迭代时的 i 值。
定时器回调中如何访问外部对象状态
当定时器需要访问外部对象的状态时,闭包会自动捕获该对象的引用。如果对象属性在定时器触发前被修改,回调将看到最新的值;如果需要保留特定时刻的状态,应在定时器创建时将所需状态复制到局部变量中。这种机制使得定时器能够灵活地读取和响应外部数据的变化。


















