Js中forEach比for循环快?

299 阅读2分钟

在JavaScript中,for循环和forEach循环的性能比较

for循环是通过生成数组的索引下标来循环遍历数组的每一个数据元素。它不会产生任何上下文开销因为它直接操作数组元素,而不需要额外的函数调用。

相比之下,forEach是JavaScript定义的数组的函数方法,通过JavaScript底层程序循环遍历数组的数据元素forEach的函数签名forEach(function(cur, i, arr), thisValue)就能看出来,里面有个function,每次循环执行时要维护块级作用域、要维护函数上下文。这就意味着,forEach在每次循环时都会产生一些额外的开销

此外,forEach循环的底层实现其实是实现了迭代器的功能。这意味着,forEach可以遍历任何实现了Iterable接口的对象。

transitioning javascript builtin
ArrayForEach(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  try {
    // 获取数组
    const o: JSReceiver = ToObject_Inline(context, receiver);

    // 获取数组长度
    const len: Number = GetLengthProperty(o);

    // 获取回调函数
    const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;

    // 获取 thisArg,可选
    const thisArg: JSAny = arguments[1];

    let k: Number = 0;
    try {
      return FastArrayForEach(o, len, callbackfn, thisArg)
          otherwise Bailout;
    } label Bailout(kValue: Smi) deferred {
      k = kValue;
    }
  }
}
  • 而FastArrayForEach的源码如下
transitioning macro FastArrayForEach(implicit context: Context)(
    o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny
    labels Bailout(Smi) {

  let k: Smi = 0;
  const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
  const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
  let fastOW = NewFastJSArrayWitness(fastO);

  // 遍历开始
  for (; k < smiLen; k++) {
    // Ensure that we haven't walked beyond a possibly updated length.
    if (k >= fastOW.Get().length) goto Bailout(k);
    // 获取当前遍历元素
    const value: JSAny = fastOW.LoadElementNoHole(k)
        otherwise continue;
    // 不要 callbackfn 返回的结果
    Call(context, callbackfn, thisArg, value, k, fastOW.Get());
  }
  // 返回 Undefined 
  // 多希望可以返回原数组啊,这样就可以链式调用了
  return Undefined;
}

核心是在for循环中,对每一个元素都调用 callbackfn

那么有了for循环,为什么还要forEach

juejin.cn/post/736838…