用 let 声明循环变量可彻底解决循环变量泄露问题,因其提供真正的块级作用域,每次迭代创建独立绑定,变量执行完即失效;而 var 只有函数作用域,循环共用一个变量导致异步场景输出终值。

用 let 声明循环变量,就能彻底解决循环变量泄露问题。根本原因在于 let 提供真正的块级作用域,让每次迭代的变量彼此隔离、执行完即失效,不再污染外部。
为什么 var 会导致泄露
var 只有函数作用域(或全局作用域),没有块级作用域。for 循环体内的 {} 并不构成独立作用域:
- 循环变量
i实际声明在函数顶层,整个循环共用一个i - 循环结束后
i仍可访问,值为终值(如i === 5) - 更严重的是异步场景:所有
setTimeout回调共享同一个i,最终都输出终值
let 如何从机制上解决
let 每次循环都会为变量创建**新的绑定(binding)**,不是简单地重复赋值:
- 每次迭代的
i是独立的变量,互不影响 - 循环体结束,该次迭代的
i就不可访问(console.log(i)报ReferenceError) - 闭包中捕获的也是当次迭代的
i,所以funcs[0]()输出0而非3
推荐写法与注意事项
日常开发中优先采用语义清晰、安全可靠的现代写法:
- 直接用
for (let i = 0; i —— 简单明确,兼容性好(2015 年起主流环境均支持) - 遍历数组优先用
for (const item of array)或array.forEach(),避免手动管理索引 - 不要在循环体内重复声明同名变量(即使用
let),保持变量声明单一、意图清晰 - IIFE 包裹循环已完全过时,仅在需支持 IE8 等极老环境时才考虑回退
不复杂但容易忽略。关键就一条:把 var i 换成 let i,泄露问题自然消失。











