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

迭代期间新增元素时循环次数差异:forEach与for/of行为原因求解

forEach 与 for...of 迭代新增元素行为差异的底层原因

你观察到的行为是ECMAScript标准明确规定的正常表现,两者底层执行逻辑的差异导致了处理迭代过程中数组变动的区别。

复现代码

let my_array1 = [1,2,3]
let my_array2 = ["a", "b", "c"]

// 不会动态新增迭代周期,循环将按预定义的次数执行结束
my_array1.forEach((value, index, the_array) => {
  if (value == 3){
    the_array.push(3)  // 这种写法效果一致
  }
})

console.log(`my_array1 after iteration ${my_array1}`)

// 会动态新增迭代周期,将触发无限循环!!!
for (let value of my_array2){
  if (value == "c"){
    my_array2.push("c")
  }
}

console.log(`my_array2 after iteration ${my_array2}`)

Array.prototype.forEach 的执行规则

根据ES规范,forEach 调用时会优先执行以下两步:

  1. 先获取当前数组的 length 属性值,作为固定的迭代次数上限存储下来,后续迭代过程中不会再更新该值
  2. 按照索引从0到「提前存储的length值-1」的顺序依次调用回调函数,哪怕迭代过程中数组新增了元素、length 变大,超出原始长度的索引也不会被遍历到

如果迭代过程中数组元素被删除,对应索引位置找不到有效值时,回调会跳过该次执行。

for...of 的执行规则

for...of 循环的本质是调用遍历对象的迭代器(Iterator)接口,数组的默认迭代器执行逻辑为:

  1. 初始化当前遍历索引为0
  2. 每次迭代时调用迭代器的next()方法,此时才会判断当前索引是否小于数组实时的length值
    • 是:返回当前索引对应的值,索引自增1,进入下一轮迭代
    • 否:返回done: true,结束循环

因此你在迭代到值为"c"的元素时持续往数组末尾新增元素,数组的实时length会不断变大,迭代器的索引永远达不到大于等于length的阈值,就会触发无限循环。

额外补充

普通的for(let i = 0; i < arr.length; i++)循环和for...of表现一致,因为每次循环判断条件时都会读取数组最新的length值;如果需要避免for...of的动态遍历特性,可以提前对数组做浅拷贝再遍历,比如for (let value of [...my_array2]),此时迭代的是固定长度的拷贝数组,不会受原数组变动影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 18:06:07