var声明的循环变量总输出同一值,因其函数作用域导致所有迭代共享同一i变量,异步回调执行时i已为终值;let则每次迭代创建新绑定,闭包捕获各自独立的值。

var 声明的循环变量为什么总输出同一个值
用 var 声明循环变量时,变量具有函数作用域,整个 for 循环只创建一个 i。所有异步操作(如 setTimeout、事件监听器)中的回调函数都引用这个唯一的 i。等循环结束,i 已变成终值(比如 5 或 10),而回调尚未执行,最终全部打印该终值。
- 变量被提升到函数顶部,反复赋值而非新建
- 闭包捕获的是变量的“引用”,不是某次迭代的“快照”
- 常见表现:循环 0–4,却连续输出
5
let 为何能自然解决这个问题
let 每次迭代都创建一个新的绑定,相当于为每次循环生成独立的变量实例。每个 setTimeout 回调形成的闭包,捕获的是各自迭代中那个独一无二的 i 值。
- 块级作用域保证每次循环有独立内存空间
- 闭包捕获的是“绑定”,而非共享引用
- 无需额外封装,代码简洁直观
用闭包手动修复 var 的问题
如果受限于 ES5 环境或需兼容旧逻辑,可通过立即执行函数(IIFE)把当前 i 作为参数传入,形成局部作用域,让闭包捕获该参数的副本。
- 写法示例:
for (var i = 0; i console.log(j), 100); })(i); } - 本质是把外部变量“固化”为形参
j,每个回调闭包都拥有自己的j - 箭头函数本身不绑定
this或arguments,但在此场景中不影响变量捕获逻辑
实际开发中的选择建议
现代项目优先使用 let ——它从语言层面消除了循环变量异步捕获的经典陷阱。只有在明确需要函数作用域共享(极少见),或维护老旧代码时,才考虑 var + 闭包封装的方案。
- 避免为“习惯”而用
var,尤其涉及定时器、Promise、事件绑定 - 注意:
let在 for...in / for...of 中同样生效,行为一致 - 调试时可加
console.log({i})观察每次迭代的变量状态,验证是否真正隔离

















