直接用 for 替代 forEach 能显著降低 V8 的迭代器开销,因其避免隐式创建迭代器、减少函数调用层级,并利于 TurboFan 内联与类型优化;forEach 则需创建作用域、生成临时状态、承担调用开销及潜在内存逃逸。

直接用 for 替代 forEach 能显著降低 V8 的迭代器开销,核心在于避免隐式创建迭代器对象、减少函数调用层级,并让 TurboFan 更容易做内联与类型优化。
为什么 forEach 会引入额外开销
forEach 是数组原型上的高阶函数,每次调用都会:
- 创建一个新函数作用域(即使箭头函数也需绑定上下文);
- 在内部触发 Array.prototype[Symbol.iterator] 或手动索引逻辑,生成临时迭代状态;
- 每次迭代都发生一次独立的函数调用,带来约 0.05 微秒的调用开销(Chrome 115 测得);
- 回调函数若含闭包或动态访问外部变量,还可能引发内存逃逸,阻碍 V8 的优化编译。
用 for 实现更可控的底层遍历
for 循环把控制权完全交还给开发者,V8 可以:
- 将循环条件和步进逻辑静态分析,提前做边界检查消除(如已知数组长度不变);
- 对 array[i] 访问生成稳定 IC(Inline Cache)条目,加速属性查找;
- 在类型稳定前提下,将整个循环体内联进周边代码,甚至向量化处理(尤其配合 TypedArray)。
推荐写法示例:
const arr = new Float64Array(1_000_000);
// ✅ 预缓存 length,使用 let i(非 var,避免函数作用域问题)
for (let i = 0, len = arr.length; i < len; i++) {
arr[i] = Math.sqrt(i);
}
for-of 并不总是比 for 快
for-of 看似简洁,但它仍依赖迭代协议:
- 每次迭代调用 next() 方法,产生对象({ value, done }),构成堆分配;
- 对普通数组,V8 虽做了部分优化,但无法绕过 Symbol.iterator 查找与状态维护;
- 若遍历的是自定义可迭代对象(如生成器、Map),开销进一步放大。
仅在以下情况可考虑 for-of:
- 数据源天然为迭代器(如 document.querySelectorAll(...) 返回的 NodeList);
- 语义清晰性优先于极致性能,且数据量较小(
配合其他关键点才能真正释放 for 的优势
单换循环语法不够,还需同步注意:
- 不在循环内调用 arr.length —— 提前缓存,避免每次读取属性;
- 避免在循环中修改数组结构(如 push、splice),否则破坏类型稳定性,触发反优化;
- 若需累积状态(如求和),用局部变量而非新建对象:let sum = 0; 而非 const acc = { sum: 0 };;
- 处理数值密集场景时,搭配 Float32Array 或 Int32Array,使内存连续、无 GC 压力。

















