let 声明循环变量可为每次迭代创建独立块级作用域绑定,使定时器回调正确读取当轮索引;var 因仅有函数作用域,导致所有回调共享同一变量i,最终输出全为终值。

用 let 声明循环变量,就能让每次迭代拥有独立的绑定,定时器回调自然读取到对应轮次的值——不用手动写闭包,代码更简洁、逻辑更清晰。
为什么 var 在定时器里会输出错误索引
var 声明的变量只有函数作用域,没有块级作用域。for 循环中用 var 声明 i,整个循环共享一个 i 变量:
用于 inference.sh 的 JavaScript/TypeScript SDK,可运行 AI 应用、构建代理、集成 150+ 模型。包名:@inferencesh/sdk(npm install),完整 TypeScript 支持。
- 循环快速执行完,i 最终变成 arr.length
- 所有 setTimeout 回调都引用同一个全局(或函数级)i
- 等 1 秒后真正执行时,i 已是最终值,导致输出全是 undefined 或重复数字
let 如何一步解决这个问题
let 为每次循环创建独立的块级作用域,每个 i 都是新绑定的变量,生命周期与当次迭代绑定:
- for (let i = 0; i
- setTimeout 回调内部访问的 i,就是定义它时那一轮的 i 值
- 无需额外函数包装,无 IIFE,无参数传递,语义直白
function print(arr) {
for (let i = 0; i < arr.length; i++) {
setTimeout(() => console.log(arr[i]), 1000 * i);
}
}
print(['a', 'b', 'c']); // 1s 后输出 'a',2s 后 'b',3s 后 'c'对比:ES5 闭包方案(作为参照)
ES5 没有块级作用域,只能靠立即执行函数(IIFE)手动制造作用域:
- 把当前 i 作为参数传入 IIFE,形成局部变量 index
- setTimeout 回调捕获的是这个局部 index,而非外部循环变量
- 本质是用函数作用域模拟块级作用域,代码冗余、理解成本高
for (var i = 0; i < arr.length; i++) {
(function(index) {
setTimeout(() => console.log(arr[index]), 1000 * index);
})(i);
}注意事项
- let 的作用域边界是 { },包括 for、if、while 等任意代码块
- 不能在声明前访问 let 变量(暂时性死区),但对定时器无影响,因为回调执行在声明之后
- const 同样具备块级作用域,但只适用于不重新赋值的场景;循环计数器必须用 let

















