JavaScript闭包在循环中绑定变量时,所有闭包共享最终值;用let声明、IIFE或forEach可为每次迭代创建独立作用域,推荐默认使用let并配合ESLint规范。

JavaScript中闭包在循环体内绑定变量时,最典型的陷阱是:循环结束时所有闭包引用的都是循环变量的最终值,而不是每次迭代时的值。
问题根源:变量提升与作用域共享
在 var 声明的循环(如 for (var i = 0; i )中,变量 <code>i 是函数作用域的,整个循环共用同一个 i。当异步操作(如 setTimeout)或闭包延迟执行时,循环早已结束,i 已变为 3,所有闭包都读取到这个最终值。
-
var声明不具有块级作用域,i在整个函数内可见且唯一 - 闭包捕获的是变量的引用,不是创建时的快照值
- 回调函数实际执行时,循环早已完成,
i === 3
经典示例与错误表现
以下代码本意是依次输出 0、1、2,但实际全部输出 3:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
原因:三个 setTimeout 回调共享同一个 i,执行时 i 已为 3。
立即学习“Java免费学习笔记(深入)”;
可靠解决方案
核心思路是为每次迭代创建独立的作用域,使每个闭包绑定各自的变量副本。
-
使用
let声明循环变量:ES6 中let具有块级作用域,每次迭代都会创建新的绑定for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // 输出 0, 1, 2 } -
立即执行函数(IIFE)封装:将当前
i作为参数传入,形成闭包私有变量for (var i = 0; i < 3; i++) { (function(i) { setTimeout(() => console.log(i), 100); })(i); } -
使用
forEach等数组方法:天然提供独立形参作用域[0, 1, 2].forEach(i => setTimeout(() => console.log(i), 100));
额外注意点
该陷阱不仅出现在 setTimeout,也常见于事件监听器、Promise 回调、Ajax 成功处理等异步场景;在 for...in、for...of 中若用 var 同样存在风险。现代开发推荐默认使用 let 替代 var,并借助 ESLint 规则(如 no-var)预防此类问题。










