You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS循环与闭包疑问:为何setTimeout输出10而非9?

关于JavaScript循环与闭包的几个常见困惑解答

嘿,这几个问题我刚入门JS的时候都踩过坑,太有共鸣了!咱们一个个来,优先把最核心的第一个问题掰透~

问题1:为什么for (var i=0; i < 10; i++){ setTimeout(function(){ console.log(i); }, 1000); }会十次输出10?

这事儿核心要抓两点:var的作用域特性和**setTimeout的异步执行逻辑**。

首先,var声明的变量是「函数级作用域」(如果这段代码不在函数里就是全局作用域),整个循环从头到尾只有同一个i变量,不是每次循环都新建一个i。

然后,setTimeout是异步任务——它不会立刻执行里面的回调函数,而是把回调扔进浏览器的任务队列,等当前的同步代码(也就是整个for循环)跑完之后,才会依次执行这些回调。

那循环的执行过程是啥样的?

  • 一开始i=0,满足i<10,执行一次setTimeout(把回调存起来),然后i变成1;
  • 重复这个过程,直到i=9的时候,还是满足i<10,执行第10次setTimeout,然后i变成10;
  • 这时候i<10不成立,循环停止。

等1秒后,任务队列里的10个回调开始执行,它们要找的都是同一个i变量,而这时候i已经是10了——所以每个回调都会输出10,一共十次。

为啥不是九次9?因为循环一共执行了10次(i从0到9,每次对应一次setTimeout),所以有10个回调,自然输出十次10。

问题2:为什么同步的for(var i=0; i<10; i++){ console.log(i) }会依次输出0-9?

这个就简单了——因为这里的console.log是同步执行的,每次循环迭代里,console.log都会立刻拿到当前i的值:

  • 第一次循环i=0,直接输出0;
  • 第二次i=1,直接输出1;
  • ...直到i=9,输出9,然后i变成10,循环停止。

和第一个问题的本质区别就是:同步代码按顺序立刻执行,异步代码要等同步代码跑完才执行,这时候i已经变了。

问题3:为什么for(let i=0; i<2; i++){ setTimeout(()=>console.log(i)) i++ }输出1而非2?

这里要抓let的「块级作用域」特性和循环的执行流程:

首先,let声明的变量是「块级作用域」,每次for循环迭代都会新建一个独立的i变量,每个回调绑定的都是当前迭代的那个i。

然后看这段代码的执行步骤:

  1. 初始化let i=0,第一次判断0<2,满足条件进入循环体;
  2. 执行setTimeout(把回调存起来,绑定当前迭代的i);
  3. 循环体里执行i++,当前迭代的i变成1;
  4. 执行for循环自带的后置i++(这一步是为下一次迭代准备的,把下一个i变成2);
  5. 第二次判断2<2,不满足,循环直接停止——所以整个循环只跑了1次,只有1个回调;
  6. 等当前同步代码跑完,回调执行,它绑定的是第一次迭代里被改成1的i,所以输出1。

为啥不是2?因为循环只执行了一次,而且回调绑定的是第一次迭代的i(最后被改成了1),下一次迭代根本没启动,自然不会输出2。


内容的提问来源于stack exchange,提问作者Robert O'Toole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:41:04