web-dev-qa-db-ja.com

JavaScript-myArray.forEachとforループのニュアンス

私は次の使用を提案する多くの質問を見てきました:

for (var i = 0; i < myArray.length; i++){ /* ... */ }

の代わりに:

for (var i in myArray){ /* ... */ }

配列の場合、一貫性のない反復のため( こちらを参照 )。


ただし、オブジェクト指向のループを好むと思われるものを見つけることができないようです。

myArray.forEach(function(item, index){ /* ... */ });

それは私にとってはもっと直感的に思えます。

私の現在のプロジェクトでは、IE8の互換性が重要であり、 Mozillaのポリフィル の使用を検討していますが、これがどのように機能するかは100%確信できません。

  • 標準forループ(上記の最初の例)と最新のブラウザーによるArray.prototype.forEachの実装に違いはありますか?
  • 最新のブラウザの実装と上記にリンクされているMozillaの実装との間に違いはありますか(特にIE8に関して)?
  • パフォーマンスはそれほど大きな問題ではなく、プロパティが反復処理される一貫性です。
88
Michael Lewis

forループとforEachメソッドの最も本質的な違いは、前者では、ループからbreakできることです。 continueに渡された関数から戻るだけでforEachをシミュレートできますが、ループを完全に停止する方法はありません。

それとは別に、2つは同じ機能を効果的に達成します。もう1つの小さな違いは、変数の巻き上げのために、forループのインデックス(およびすべての変数を含む)のスコープに関係します。

// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }

// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });

ただし、forEachの方がはるかに表現力が高いことがわかります。これは、配列の各要素を反復処理する意図を表し、インデックスだけでなく要素への参照を提供します。全体的に、それは主に個人的な好みに帰着しますが、forEachを使用できる場合は、使用することをお勧めします。


2つのバージョンには、特にパフォーマンスに関して、いくつかの実質的な違いがあります。実際、単純なforループは、 this jsperf test で示されるように、forEachメソッドよりもかなり優れたパフォーマンスを発揮します。

そのようなパフォーマンスがあなたに必要かどうかはあなたの判断次第であり、ほとんどの場合、スピードよりも表現力を優先します。この速度の違いは、 this answer に記載されているように、スパース配列を操作するときの基本ループとメソッドのわずかなセマンティックの違いによる可能性があります。

forEachの動作が不要な場合、および/またはループから早く抜け出す必要がある場合は、Lo-Dashの _.each を代替として使用できます。 -ブラウザ。 jQueryを使用している場合、同様の $.each も提供されます。各バリエーションでコールバック関数に渡される引数の違いに注意してください。

forEachポリフィルについては、その経路に進むことを選択した場合、問題なく古いブラウザーで動作するはずです。)

121
Alexis King

Array.forEachよりも優れたパフォーマンスを発揮するカスタムforeach関数を使用できます。

これをコードに一度追加する必要があります。これにより、新しい関数が配列に追加されます。

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for(var i=0; i<len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

その後、Array.forEachのようにどこでも使用できます

[1,2,3].customForEach(function(val, i){

});

唯一の違いは、3倍高速です。 https://jsperf.com/native-arr-foreach-vs-custom-foreach

更新:新しいChromeバージョンでは、.forEach()のパフォーマンスが改善されました。ただし、このソリューションは、他のブラウザーで追加のパフォーマンスを提供できます。

JSPerf

12
Seagull

多くの開発者(Kyle Simpsonなど)は、.forEachを使用して配列に副作用があることを示し、.mapを純粋な関数に使用することをお勧めします。 forループは、既知の数のループや、ほとんどのプログラミング言語で幅広くサポートされているため通信が容易であるため適合しないその他のケースの汎用ソリューションに適しています。

例えば.

/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
  //Do Something
}

/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));

/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.Push(el.toUpperCase()));

慣習的にはこれが最良のように見えますが、コミュニティは新しい反復プロトコルfor inループの慣習に実際には決着していません。一般に、JSコミュニティが採用を受け入れていると思われるFPの概念に従うことは良い考えだと思います。

4
steviejay