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

var变量在for循环与setTimeout中输出值异常的技术问询

为什么setTimeout内部的i输出为4而不是3?

这个问题是JavaScript里作用域机制和事件循环结合的经典坑,我来给你一步步理清楚原因:

核心原因拆解

先看你的代码:

var arr = [ 10, 12, 15, 21 ]; 
for(var i = 0; i < arr.length; i++) { 
  setTimeout(function(){ 
    console.log('Index: ' + i + ', element: ' + arr[i]) 
  }, 3000) 
}
  1. var的作用域缺陷:用var声明的i是函数级/全局作用域,整个循环过程中只有一个i变量,而不是每次循环都生成新的i。也就是说,四个setTimeout的回调函数,共享同一个i的引用。
  2. 事件循环的执行顺序:setTimeout属于宏任务,它的回调不会立刻执行,而是被放到浏览器的宏任务队列中,要等当前所有的同步代码(也就是整个for循环)执行完毕后,才会开始处理队列里的回调。
  3. 循环结束时的i值:循环的终止条件是i < arr.length,arr的长度是4。当i递增到4时,4 < 4不成立,循环停止。这时候i的值已经是4了。
  4. 回调执行时的取值:3秒后,四个定时器回调依次执行,它们访问的都是那个已经变成4的i变量,所以输出的Index全是4,arr[4]自然就是undefined。

几种可行的解决办法

想要让每个回调拿到对应循环次数的索引,有这些常用方案:

1. 用let声明i(ES6+推荐方案)

let支持块级作用域,每次循环都会创建一个全新的i变量,每个setTimeout回调捕获的都是当前循环的那个i:

var arr = [ 10, 12, 15, 21 ]; 
for(let i = 0; i < arr.length; i++) { 
  setTimeout(function(){ 
    console.log('Index: ' + i + ', element: ' + arr[i]) 
  }, 3000) 
}

2. 用闭包创建独立作用域(ES6之前的解决方案)

通过立即执行函数表达式(IIFE)为每次循环创建私有作用域,把当前的i作为参数传进去,让回调能捕获到这个局部的索引值:

var arr = [ 10, 12, 15, 21 ]; 
for(var i = 0; i < arr.length; i++) { 
  (function(currentIndex) {
    setTimeout(function(){ 
      console.log('Index: ' + currentIndex + ', element: ' + arr[currentIndex]) 
    }, 3000)
  })(i);
}

3. 使用forEach遍历数组

forEach的回调函数本身就自带独立作用域,每次遍历都能直接拿到当前的索引和元素,写法更简洁:

var arr = [ 10, 12, 15, 21 ]; 
arr.forEach(function(element, index) {
  setTimeout(function(){ 
    console.log('Index: ' + index + ', element: ' + element) 
  }, 3000)
});

内容的提问来源于stack exchange,提问作者Aessandro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:03:58