let通过为每次for循环迭代创建独立块级作用域绑定,使每个闭包捕获各自迭代的i实例;而var因函数作用域共享同一i变量,导致异步回调输出终值。

ES6 中用 let 解决循环变量泄露,核心在于它为每次循环迭代创建独立的块级作用域,让每个闭包捕获的是各自迭代中独立的变量实例,而不是共享同一个变量引用。
循环变量泄露的本质问题
在 ES5 及之前,用 var 声明循环变量时,变量属于函数作用域(或全局),整个循环只存在一个 i。所有异步回调(如 setTimeout)都引用这同一个 i,等回调执行时循环早已结束,i 已变成终值(如 10),导致全部输出相同结果。
-
for (var i = 0; i console.log(i), 0); }→ 输出3, 3, 3 - 根本原因:闭包捕获的是变量本身,而
var i全局唯一、被反复赋值
let 如何逐次绑定独立变量
let 在每次 for 迭代开始时,都会为该轮迭代创建一个新的绑定(binding),即一个新的 i 实例。每个回调函数闭包捕获的,是各自迭代中那个“专属”的 i,而非共享变量。
-
for (let i = 0; i console.log(i), 0); }→ 输出0, 1, 2 - 这并非“复制值”,而是每次迭代生成新的词法环境,每个
i有独立内存位置和生命周期 - 即使不写花括号,
for循环体本身就是一个隐式块,let 的绑定就发生在这个块内
需注意的关键细节
let 的修复能力依赖于标准的 for 语法结构。某些写法会绕过机制,导致失效:
- ❌ 错误:把
let i提到循环外再手动递增 —— 失去迭代绑定,退化为单个变量let i = 0; while (i console.log(i), 0); i++; }→ 仍输出3, 3, 3 - ✅ 正确:必须使用
for (let i = ...)形式,让引擎识别为“带词法绑定的迭代” - ⚠️ 补充:箭头函数不是必需的,普通函数也一样有效;关键是
let在for中的声明方式
兼容性与替代方案
现代浏览器和 Node.js(v4+)均原生支持。若需兼容极旧环境,可用 IIFE 封装(ES5 方案),但语义不如 let 清晰:
for (var i = 0; i console.log(i), 0); })(i); }- 这种手动闭包模拟了 let 的行为,但代码冗长且易出错











