闭包捕获变量引用而非值,for循环中var声明导致所有闭包共享同一i,输出全为终值3;应改用let、IIFE或forEach创建独立作用域,并注意闭包引发的内存驻留问题。

闭包本身不会“动态更新”作用域链,作用域链在函数定义时就已静态确定。但开发者常误以为它会随循环变量变化而实时更新——这恰恰是问题根源。
闭包捕获的是变量的引用,不是值
在 for 循环中直接创建闭包(尤其用 var 声明计数器),所有闭包共享同一个变量绑定。循环结束时,变量值已定格为最终值,导致所有闭包访问到的都是这个终值。
- 典型问题代码:
for (var i = 0; i setTimeout(() => console.log(i), 100);
} → 输出三次 3,而非 0、1、2 - 原因:三个定时器回调都形成闭包,但它们共用外层函数作用域中的同一个 i 变量;循环结束后 i === 3,所有回调读取的都是这个值
正确做法:隔离每次迭代的词法环境
要让每个闭包拥有独立的变量副本,必须为每次迭代创建独立的作用域。
- ✅ 推荐:用 let 声明循环变量
let 在每次迭代中声明新绑定,每个闭包捕获的是各自迭代独有的 i - ✅ 兼容旧环境:IIFE(立即执行函数)传参
for (var i = 0; i (function(j) {
setTimeout(() => console.log(j), 100);
})(i);
} - ✅ 更现代:使用 forEach 或 for...of
天然为每次迭代提供独立作用域,避免手动处理
注意闭包对内存的影响
每个闭包都会保留对外部词法环境的引用。若循环次数多、外部变量大,或闭包长期存活(如绑定事件、缓存引用),可能造成意外内存驻留。
- 确认闭包是否真需访问循环外的大对象;如只需原始值,提前解构或拷贝
- 不再需要闭包时,主动解除对外部变量的引用(例如将保存闭包的变量设为 null)
- 避免在闭包内无意修改外部变量,尤其当多个闭包共享同一变量时,行为易失控

















