在JavaScript中,for循环和forEach循环的性能比较
for循环是通过生成数组的索引下标来循环遍历数组的每一个数据元素。它不会产生任何上下文开销因为它直接操作数组元素,而不需要额外的函数调用。
相比之下,forEach是JavaScript定义的数组的函数方法,通过JavaScript底层程序循环遍历数组的数据元素forEach的函数签名forEach(function(cur, i, arr), thisValue)就能看出来,里面有个function,每次循环执行时要维护块级作用域、要维护函数上下文。这就意味着,forEach在每次循环时都会产生一些额外的开销。
此外,forEach循环的底层实现其实是实现了迭代器的功能。这意味着,forEach可以遍历任何实现了Iterable接口的对象。
- Javascript Array.prototype.forEach 实际调用的是 V8 的 ArrayForEach tc39.github.io/ecma262/#se…
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