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

JavaScript中for循环变量迭代行为及闭包相关疑问

JavaScript中for循环变量迭代行为及闭包相关疑问

嘿,我最近在啃JavaScript闭包的知识点时,碰到了两个对比感拉满的for循环例子,结果和我预期的不一样,想跟大家唠唠这个困惑~

先看第一个例子,这段代码的输出完全符合预期:

for (let i = 0; i < 3; i++) {
  const log = () => {
    console.log(i);
  };
  setTimeout(log, 100);
}

输出是:

$ 0
$ 1
$ 2

我理解的是,这里用let声明的i是作用域绑定到每一次迭代的——JS引擎会在每次循环迭代时,都给i创建一个新的变量引用,而不是所有迭代共用同一个i。所以每个log函数作为闭包,捕获的都是当前迭代里i的专属引用,等setTimeout的回调执行时,拿到的就是对应迭代的i值啦。

但接下来这个例子就把我整懵了:

for (let arr = [1, 2, 3]; arr.length !== 0; arr.pop()) {
  const log = () => {
    console.log(arr);
  };
  setTimeout(log, 1000);
}

我本来以为会输出:

$ [1, 2, 3]
$ [1, 2]
$ [1]

结果却是三次输出空数组!这明显说明arr变量是所有迭代共用的,和第一个例子的i完全不一样,这到底是为啥?

后来我想明白了,问题出在for循环的变量初始化逻辑上:

  • 第一个例子是标准的三段式for循环(初始化let i=0、条件判断、增量i++),这种情况下,let声明的变量会在每一次迭代开始前被重新创建并赋值,相当于每次迭代都有一个独立的i。
  • 而第二个例子里,let arr = [1,2,3]这个初始化操作只在循环开始时执行一次,整个循环过程中只有一个arr变量实例!每次迭代的arr.pop()都是在修改同一个数组对象,所有的log闭包捕获的都是这个唯一的arr引用。等1秒后setTimeout执行回调时,for循环早就跑完了——arr已经被pop()到空数组了,所以三次输出都是空的。

如果想要得到预期的输出,其实可以在每次迭代里创建当前数组的副本,让每个闭包捕获独立的数组引用:

for (let arr = [1, 2, 3]; arr.length !== 0; arr.pop()) {
  // 创建当前数组的副本,而不是直接引用原数组
  const currentArr = [...arr];
  const log = () => {
    console.log(currentArr);
  };
  setTimeout(log, 1000);
}

这样每个log捕获的是当前迭代的数组副本,就会输出[1,2,3]、[1,2]、[1]啦~

备注:内容来源于stack exchange,提问作者Hayeddine Kelious

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 11:59:50