setTimeout回调函数何时获取参数?为何示例代码输出7而非预期的5
你遇到的问题本质是两个核心JS机制共同作用的结果:
核心知识点1:JS事件循环的异步任务执行规则
- JS是单线程运行的,所有同步代码会优先在主线程调用栈中依次执行,
setTimeout属于异步宏任务,调用时不会立刻执行回调,只会把回调函数暂存到宏任务队列中等待调度。 - 异步队列的任务必须等主线程上所有同步代码全部执行完成、调用栈清空后,才会被取出执行。哪怕你设置的延迟时间是0,也不会跳过这个规则。
你给出的代码实际执行顺序如下:
- 声明函数
foo,声明变量x并赋值为5 - 执行
setTimeout(foo, 0),将foo回调注册到宏任务队列 - 执行长for循环(同步代码,哪怕耗时很久也会先跑完)
- 执行
x = 7,同步代码全部执行完毕 - 主线程空闲,取出宏任务队列里的
foo执行,此时读取到的x已经是修改后的值7。
核心知识点2:闭包的变量引用捕获规则
你定义的foo函数引用了外部作用域的变量x,函数捕获的是x的内存引用,而非setTimeout调用那一刻的x的数值快照。因此回调函数实际执行时,会读取x当前的最新值,而非注册回调时的历史值。
如果要输出预期的5,可以参考如下改造方案
在注册setTimeout时就把当前x的值快照保存下来即可:
let foo = (val) => { console.log(val); }; let x = 5; // 注册回调时立即读取当前x的值,作为参数传入待执行的函数 setTimeout(() => foo(x), 0); for (let i = 0; i < 10000000000; i++) {} x = 7;
内容的提问来源于stack exchange,提问作者Alex K
相关产品推荐
相关产品推荐

