请求解析setTimeout与for循环的交互逻辑,疑惑JS代码result输出为9的原因
代码运行逻辑拆解与误区纠正
两个核心认知偏差
- 你对setTimeout的执行次数判断完全错误:不存在“每轮循环执行3次、累计9次”的可能
- 你没有理清var的作用域规则、JS事件循环的执行顺序,因此无法推导最终结果
setTimeout的实际执行次数
setTimeout是同步执行的API,核心作用是把传入的回调注册到1秒后的延时任务队列,不会在调用时立刻执行回调。
这段代码的for循环总共执行3次,每次循环仅调用1次setTimeout,也就是总共只注册了3个延时回调,整个运行过程中这些回调只会执行3次。你之前的误解是错误叠加了循环次数和回调执行次数——实际调用多少次setTimeout,就只会生成多少个待执行的回调,不会多也不会少。
为什么最终result值为9
要搞懂这个结果,需要结合两个JS的核心规则拆解:
1. 事件循环的执行顺序
JS是单线程运行的,会优先执行完全部同步代码,再去执行异步队列里的回调。这段代码的同步执行流是:
- 全局初始化
result = 0 - 通过var声明全局变量i,进入for循环
- i=0时,注册第一个1秒延时的回调,不执行回调,循环继续
- i=1时,注册第二个1秒延时的回调,不执行回调,循环继续
- i=2时,注册第三个1秒延时的回调,不执行回调,循环继续
- i自增到3,不满足
i < 3的循环条件,退出循环 - 立刻执行同步代码
console.log(result)——此时三个延时回调还没到执行时间,这行代码实际输出的是0,不是9
等1秒后,所有同步代码早就执行完毕,三个注册好的延时回调才会依次从异步队列取出执行。
2. var的作用域特性
你用var声明的i没有块级作用域,这个i是全局作用域下的同一个变量,不会因为循环轮次生成独立的i值。等到1秒后三个回调真正执行时,for循环早已跑完,i的值已经停留在退出循环时的3。
因此每个回调的执行逻辑都是result += 3:
- 第一次回调执行:result = 0 + 3 = 3
- 第二次回调执行:result = 3 + 3 = 6
- 第三次回调执行:result = 6 + 3 = 9
这就是最终result为9的原因。
补充对比验证:如果把循环里的
var i改成let i,因为let是块级作用域,每次循环都会生成一个独立的i绑定,三个回调拿到的i值分别是0、1、2,最终result的结果会是0+1+2=3。
内容的提问来源于stack exchange,提问作者coder87
相关产品推荐
相关产品推荐

