独自のスコープでforループを実行する最速の方法を見つけようとしていました。比較した3つの方法は次のとおりです。
var a = "t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t".split();
// lodash .each -> 1,294,971 ops/sec
lodash.each(a, function(item) { cb(item); });
// native .forEach -> 398,167 ops/sec
a.forEach(function(item) { cb(item); });
// native for -> 1,140,382 ops/sec
var lambda = function(item) { cb(item); };
for (var ix = 0, len = a.length; ix < len; ix++) {
lambda(a[ix]);
}
これはOS XのChrome 29にあります。ここでテストを実行できます。
Lodashの.each
は、ネイティブ.forEach
のほぼ2倍の速度ですか?さらに、プレーンfor
よりも高速です。魔術?黒魔術?
_.each()
は[].forEach()
と完全に互換性がありません。次の例を参照してください。
var a = ['a0'];
a[3] = 'a3';
_.each(a, console.log); // runs 4 times
a.forEach(console.log); // runs twice -- that's just how [].forEach() is specified
そのため、lodashの実装にはif (... in ...)
チェックがありません。これはパフォーマンスの違いを説明する可能性があります。
上記のコメントで述べたように、ネイティブfor
との違いは、主にテストでの追加の関数ルックアップが原因です。このバージョンを使用して、より正確な結果を取得します。
for (var ix = 0, len = a.length; ix < len; ix++) {
cb(a[ix]);
}
http://kitcambridge.be/blog/say-hello-to-lo-dash/
Lo-dash開発者は、ネイティブforEach
の相対的な速度がブラウザによって異なることを説明しています(こことビデオで)。 forEach
がネイティブだからといって、for
またはwhile
で構築された単純なループよりも高速であることを意味しません。一つには、forEach
はより特殊なケースに対処する必要があります。第二に、forEach
はコールバックを使用し、関数呼び出しなどの(潜在的な)オーバーヘッドを伴います。
chrome
は、特に(少なくともlo-dash開発者には)比較的遅いforEach
を持つことが知られています。そのため、そのブラウザでは、lo-dashは独自の単純なwhile
ループを使用して速度を上げます。したがって、表示される速度の利点(ただし、他の人には表示されません)。
特定の環境で高速であることがわかっている場合にのみネイティブ実装を使用してネイティブメソッドを賢く選択することにより、Lo-Dashはネイティブに関連するパフォーマンスコストと一貫性の問題を回避します。
はい、lodash/underscoreはそれぞれ.forEach
と同じセマンティクスを持っていません。エンジンがゲッターなしでスパース配列を迅速にチェックできない限り、関数を本当に遅くする微妙な詳細があります。
これは99%仕様に準拠しており、 V8でそれぞれlodashと同じ速度で実行されます 一般的な場合:
function FastAlmostSpecForEach( fn, ctx ) {
"use strict";
if( arguments.length > 1 ) return slowCaseForEach();
if( typeof this !== "object" ) return slowCaseForEach();
if( this === null ) throw new Error("this is null or not defined");
if( typeof fn !== "function" ) throw new Error("is not a function");
var len = this.length;
if( ( len >>> 0 ) !== len ) return slowCaseForEach();
for( var i = 0; i < len; ++i ) {
var item = this[i];
//Semantics are not exactly the same,
//Fully spec compliant will not invoke getters
//but this will.. however that is an insane Edge case
if( item === void 0 && !(i in this) ) {
continue;
}
fn( item, i, this );
}
}
Array.prototype.fastSpecForEach = FastAlmostSpecForEach;
最初に未定義をチェックすることにより、ループ内の通常の配列を罰しません。エンジンは内部を使用して奇妙な配列を検出できますが、V8は検出しません。
for(...)
、Array.forEach
、および_.each
の3つすべてを比較するパフォーマンスの違いを示す更新されたリンク(2015年頃)は次のとおりです。 https://jsperf.com/native-vs -underscore-vs-lodash
注:受け入れられた答えにコメントするのに十分なポイントがまだなかったので、ここに入れてください。