JS数组遍历闭包陷阱表现为异步函数中循环变量值为最终值而非当前值;识别看三点:是否在var循环/forEach中直接使用变量、函数是否延迟执行、是否所有调用输出同一值;修复用let声明、IIFE传参或for...of解构。

JS 数组遍历中出现闭包陷阱,核心表现是:循环内创建的函数(比如事件监听器、定时器回调、验证函数)访问的变量值与预期不符——不是每次迭代时的“当前值”,而是循环结束后的最终值。
怎么识别闭包陷阱
看三点:
- 是否在
for (var i = ...)或forEach等遍历中,直接把循环变量(如i、item、index)用在了异步或延迟执行的函数里; - 该函数被定义在循环体内,但实际调用发生在循环结束后(比如点击事件、
setTimeout、Promise 回调); - 运行后发现所有函数都输出/使用同一个值(比如全是
3、undefined或最后一个item),而不是各自对应的迭代值。
常见出问题的写法
这些代码看着自然,但都踩坑:
-
for (var i = 0; i console.log(i), 100); }→ 输出全是arr.length; -
arr.forEach((item, i) => btn.addEventListener('click', () => console.log(item)));→ 所有按钮点开都输出最后一个item; - React 中在
map里定义验证函数并捕获fields状态,结果验证逻辑总读不到最新项。
三种可靠修复方式
不依赖经验猜测,选一种即可落地:
-
用
let声明循环变量:它提供块级绑定,每次迭代生成独立的绑定。例如for (let i = 0; ...),每个setTimeout拿到的是自己那轮的i; -
用立即执行函数包裹(IIFE):把当前值作为参数传进去,锁住那一刻的快照:
(function(val) { setTimeout(() => console.log(val), 100); })(arr[i]);; -
改用
for...of或解构 + 显式变量:避免直接暴露索引或引用,例如for (const item of arr) { setTimeout(() => console.log(item), 100); }——item是每次迭代的新绑定,天然安全。
额外注意:React 和状态捕获
在函数组件中,闭包陷阱常表现为“函数记住旧的 state 或 props”。比如在 useEffect 或事件处理器中直接引用 count,但后续 count 更新了,函数里还是旧值。解决方法是:
- 用
useRef同步保存最新值,函数内读ref.current; - 在需要最新状态的地方,改用函数式更新或
useCallback配合依赖数组; - 避免在闭包中长期持有对过期 state 的直接引用。


















