使用 let 声明循环变量可解决异步回调中访问循环变量值错误的问题,因其每次迭代创建块级作用域绑定;也可用 iife 或 foreach/for...of 替代 var 循环。

在 for 循环中发起异步操作(比如 setTimeout、fetch、Promise 回调),却在回调里访问循环变量(如 i 或 item)时拿到“意外”的值,这是典型的闭包导致的变量捕获问题。根本原因:循环本身很快执行完,所有回调共享同一个变量绑定,而回调执行时循环早已结束,i 已变成终值(如 arr.length)。
用 let 替代 var 声明循环变量
let 在每次迭代中创建**块级作用域绑定**,每个回调实际引用的是各自迭代中独立的 i,而不是共享一个变量。这是最简单、最推荐的修复方式。
✅ 正确写法:
for (let i = 0; i {
console.log(i); // 输出 0, 1, 2...
}, 100);
}
❌ 错误写法(var):
for (var i = 0; i {
console.log(i); // 全部输出 3(假设 arr.length === 3)
}, 100);
}
用立即执行函数(IIFE)包裹回调(兼容旧环境)
如果必须支持 ES5 或需显式控制变量传递,可将当前值作为参数传入 IIFE,形成闭包捕获当时的值。
- 适用于
var循环或需要兼容老浏览器的场景 - 注意:参数名要和循环变量区分,避免混淆
示例:
for (var i = 0; i {
console.log(index); // 输出 0, 1, 2...
}, 100);
})(i);
}
用 forEach 或其他数组方法替代传统 for 循环
forEach、map、for...of 的每次回调都有独立的作用域参数,天然避免该问题。
-
forEach的回调参数(item,index)是每次调用新生成的,不会被后续迭代覆盖 -
for...of中用let声明的遍历变量也具备块级绑定特性
示例:
arr.forEach((item, index) => {
setTimeout(() => {
console.log(index, item); // 各自正确
}, 100);
});
// 或
for (const [index, item] of arr.entries()) {
setTimeout(() => {
console.log(index, item);
}, 100);
}
调试技巧:快速定位是否为闭包问题
遇到回调中变量值“不对”时,先做三件事:
- 在循环体内打印当前
i(确认循环逻辑正常) - 在回调内打印
i和typeof i(确认不是 undefined 或 NaN) - 检查声明方式:
var→ 高风险;let/const→ 安全;再查是否在回调外提前修改了变量
如果打印显示循环中 i 递增正常,但回调里全是同一个大数(如 arr.length),基本可断定是 var 闭包问题。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











