let 为每次循环迭代创建独立的块级绑定,使每个闭包捕获各自迭代中的 i;而 var 是函数作用域且变量提升,循环共用一个 i,异步执行时 i 已为终值。

用 let 声明循环变量,能解决闭包中索引值“总是最后一个”的问题,核心在于它为每次迭代创建了**独立的块级作用域绑定**,而不是像 var 那样共享同一个变量引用。
为什么 var 在循环中会出错?
var 是函数作用域,且存在变量提升。在 for 循环中,整个循环共用一个 i 变量。当异步操作(如 setTimeout)执行时,循环早已结束,i 的值已是终值(比如 5),所有回调都读取这个最终值。
例如:
(错误示例)for (var i = 0; i console.log(i), 100); // 输出:5, 5, 5, 5, 5 }
let 是怎么修复的?
let 是块级作用域,且在每次 for 迭代开始时,都会为该次迭代**创建一个新的绑定**(不是复制值,而是新声明一个同名但独立的变量)。每个回调函数捕获的是各自迭代中那个独一无二的 i。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
例如:
(正确示例)for (let i = 0; i console.log(i), 100); // 输出:0, 1, 2, 3, 4 }
关键细节和注意事项
-
必须是 for 循环体内的 let 声明:只有
for (let i...)这种形式才触发“每次迭代新建绑定”机制;let i; for (...)写法退化为类似var行为。 -
不适用于 for-in / for-of 的旧写法:如果写成
let key; for (key in obj),依然会共享变量;应直接用for (let key in obj)或for (let [k, v] of Object.entries(obj))。 -
本质不是“保存了值”,而是“绑定了当前迭代的词法环境”:每个箭头函数内部的
i指向的是自己那轮迭代专属的绑定,因此能正确反映当时值。 - 兼容性没问题:现代浏览器和 Node.js 环境均支持,无需转译(除非需支持 IE)。
其他可行方案(对比参考)
虽然 let 是最简洁自然的解法,但了解替代方式有助于理解本质:
-
立即执行函数(IIFE)+ var:手动创建作用域,把
i作为参数传入,让每个回调持有自己的副本。 -
forEach 替代 for:数组的
forEach回调函数参数本身就是本轮的索引,天然隔离。 - 使用 const + 索引参数(如 Promise.all 配 map):在生成异步任务时就绑定当前索引,避免依赖外部变量。
不复杂但容易忽略:只要循环变量用 let 声明在 for 语句中,闭包就能拿到正确的索引值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










