settimeout回调依赖外部变量时因闭包捕获引用而非值,导致循环中输出相同索引;解决方法包括用let声明、iife传参、提前捕获值及修复this指向。

setTimeout 传入的函数如果依赖外部变量,容易因变量被后续代码修改而产生意料之外的结果——这本质上是闭包捕获的是变量的引用,而非创建时的值。
闭包捕获的是变量引用,不是值
在循环中使用 setTimeout 时,常见错误是期望每次回调打印不同的索引值,但实际全部输出最后一个值:
for (var i = 0; i console.log(i), 100); } // 输出:3, 3, 3
原因:所有回调共享同一个 i 变量(var 声明提升+函数作用域),等定时器执行时循环早已结束,i 已变为 3。
解决方法:切断对变量的引用依赖
核心思路是让每个回调拥有自己独立的变量副本:
-
用 let 替代 var:块级作用域使每次迭代生成独立的 i 绑定
for (let i = 0; i console.log(i), 100); } // 输出:0, 1, 2 -
立即执行函数(IIFE)传参:把当前值作为参数传入,形成局部变量
for (var i = 0; i console.log(i), 100); })(i); } -
箭头函数 + 参数绑定(bind):显式绑定当前值
for (var i = 0; i
对象属性引用同样危险
即使不是循环,只要闭包内访问的对象属性后续被修改,也会反映到回调中:
const obj = { value: 1 };
setTimeout(() => console.log(obj.value), 100);
obj.value = 2; // 在定时器触发前修改
// 输出:2,不是预期的 1
若需固化当时的值,应提前读取并存为局部变量:
- const captured = obj.value;
setTimeout(() => console.log(captured), 100); - 或用解构赋值提取基本类型值:const { value } = obj;
异步链中注意 this 和上下文丢失
setTimeout 回调中 this 默认指向全局对象(非严格模式)或 undefined(严格模式),容易误用对象方法:
const obj = {
name: 'test',
log() { console.log(this.name); }
};
setTimeout(obj.log, 100); // 输出 undefined 或全局 name
修复方式:
- 用箭头函数捕获外层 this:setTimeout(() => obj.log(), 100);
- bind 显式绑定:setTimeout(obj.log.bind(obj), 100);
- 封装成闭包:setTimeout(function() { obj.log(); }, 100);
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











