JavaScript数组遍历需警惕闭包、this绑定、异步执行等陷阱;for最可控但易错边界,forEach不支持await,for...of适合顺序异步,Promise.all适合并发控制,选型应基于索引需求、退出条件、异步及数组动态性。

JavaScript 数组遍历看似简单,但一不留神就掉进闭包、this 绑定、异步执行、稀疏数组、修改原数组等坑里。写法选错,轻则结果不符预期,重则引发难以排查的运行时错误。
for 循环:最可控,但易写错边界和索引
传统 for (let i = 0; i 是性能最好、语义最清晰的方式,适合需要索引、需中途跳出(break)、或需多次访问同一元素的场景。但要注意:
- 别在循环中直接修改数组长度(如
push/splice),否则可能跳过或重复遍历元素; - 用
let声明i,避免闭包中取到错误的终值(尤其配合定时器或 Promise 时); - 对稀疏数组(如
[1, , 3]),for仍会遍历空位(arr[1] === undefined),但不会触发in或forEach的“跳过”行为——这是特性,不是 bug,得按需判断。
forEach / map / filter 等高阶函数:简洁安全,但有隐式限制
它们自动跳过稀疏数组的空位,不改变原数组(除 map/filter 返回新数组外),且内部 this 可控(第二个参数可传入)。但常见误区有:
-
forEach无法用break或return中断循环(return只退出当前回调);真要提前退出,改用some或every,或退回for; - 箭头函数中
this继承外层,若回调依赖this且外层无绑定,容易出错;明确需要时,用普通函数 + 第二个thisArg参数; -
map和filter对undefined、null、false等“falsy”值不做特殊处理,仅看回调返回值是否为真值(filter)或是否被映射(map)。
for...of:兼顾语义与灵活性,推荐日常主力
ES2015 引入的 for...of 遍历的是可迭代对象的值(arr[Symbol.iterator]()),天然支持 break、continue、return,且不关心索引或稀疏性:
- 它只遍历“存在的值”,跳过稀疏空位,行为类似
forEach; - 想同时拿到索引?用
entries():for (const [i, v] of arr.entries()); - 注意:不能直接遍历普通对象(除非手动部署迭代器),也不同于
for...in(后者遍历属性名,包括原型链上的可枚举属性)。
异步遍历:别让 await 在 forEach 里“静默失效”
这是高频大坑:arr.forEach(async item => { await doAsync(item); }) 不会按顺序等待,所有异步操作几乎同时发起,且外部无法 await 整个遍历结束。
- 正确做法是用
for...of:for (const item of arr) { await doAsync(item); }; - 若需并发控制(如最多 3 个请求同时进行),用
Promise.all批量发、或借助p-limit等工具库; - 切记:
async函数返回的是 Promise,forEach不处理返回值,所以这些 Promise 被丢弃,既不等待也不报错。
防坑核心就一条:清楚每个遍历方式的执行模型——是同步逐个、异步并行、是否可中断、是否响应数组动态变化。选型前先问自己:要不要索引?会不会提前退出?有没有异步?数组是否可能稀疏或被修改?答案明确了,方法自然浮现。


















