JavaScriptで配列を反復処理するすべての方法

内容:




I.実配列の列挙


現在、実際の配列の要素を反復処理する方法は3つあります。
  1. Array.prototype.forEachメソッド;
  2. 古典的なforループ
  3. ループ内for...in 「正しく」構築さfor...in

また、まもなく、新しいECMAScript 6(ES 6)標準の出現により、さらに2つの方法が期待されます。
  1. for...ofループ(反復子の暗黙的な使用);
  2. イテレータの明示的な使用。

1. forEachメソッドと関連メソッド


プロジェクトがECMAScript 5(ES5)標準の機能をサポートするように設計されている場合、その革新の1つであるforEachメソッドを使用できます。

使用例:
 var a = ["a", "b", "c"]; a.forEach(function(entry) { console.log(entry); }); 

一般的に、 forEachを使用するには、このメソッドをネイティブにサポートしていないブラウザー用にes5-shimエミュレーションライブラリを接続する必要があります。 これらにはIE 8以前が含まれ、これらは今でもあちこちで使用されています。

forEachの利点は、配列の現在の要素のインデックスと値を格納するローカル変数を引数として自動的にコールバック関数(コールバック)に渡すため、ローカル変数を宣言する必要がないことです。

アイテムごとにコールバックを呼び出すことで発生する可能性のあるコストを心配している場合は、心配しないでこれを読んでください。

forEach 、配列のすべての要素を反復処理するように設計されていますが、それに加えて、ES5では、すべてまたは一部の要素を反復処理し、それらを使用してアクションを実行するためのいくつかのより便利なメソッドを提供しています:

2. forループ


去勢牛for古き良き

 var a = ["a", "b", "c"]; var index; for (index = 0; index < a.length; ++index) { console.log(a[index]); } 

配列全体の長さがサイクル全体で変化せず、サイクル自体がパフォーマンスの点で重要なコードセグメントに属している場合(これはほとんどありません)、 for 「より最適な」バージョンをforして配列の長さを格納できます。

 var a = ["a", "b", "c"]; var index, len; for (index = 0, len = a.length; index < len; ++index) { console.log(a[index]); } 

理論的には、このコードは前のコードよりも少し速く実行されるはずです。

要素を列挙する順序が重要でない場合は、最適化の観点からさらに進んで、列挙の順序を反対に変更することで、配列の長さを格納する変数を取り除くことができます。

 var a = ["a", "b", "c"]; var index; for (index = a.length - 1; index >= 0; --index) { console.log(a[index]); } 

ただし、最新のJavaScriptエンジンでは、このような最適化されたゲームは通常何の意味もありません。

3. for ...の適切な使用


for...inループを使用することをお勧めする場合、 配列の列挙は意図したものではないことに注意for...inください。 一般的な誤解に反して、 for...inループは配列インデックスではなく、オブジェクトの列挙プロパティを反復処理します。

ただし、以下の例に示すように、予防措置を講じる場合は、 スパース配列の列挙などの一部のケースでfor...inが役立つ場合があります。

 // a -   var a = []; a[0] = "a"; a[10] = "b"; a[10000] = "c"; for (var key in a) { if (a.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) { console.log(a[key]); } } 

この例では、ループの各反復で、2つのチェックが実行されます。
  1. 配列には、 keyと呼ばれる独自のプロパティがありkey (プロトタイプからは継承されません)。
  2. そのkeyは、値が4294967294より小さい整数の10進表記を含む文字列です。 最後の数字はどこから来たのですか? ES5の配列インデックスの定義から 、配列の要素が持つことができる最大のインデックスは(2^32 - 2) = 4294967294です。

もちろん、このようなチェックはサイクルの実行中に余分な時間がかかります。 ただし、スパース配列の場合、このメソッドはforループよりも効率的です。この場合、配列で明示的に定義されている要素のみがソートされるためです。 したがって、上記の例では、3つの反復のみが実行されます(インデックス0、10、および10000)-forループの10001に対して。

配列を反復処理する必要があるたびにこのような面倒な検証コードを記述しないようにするには、別の関数として設計できます。

 function arrayHasOwnIndex(array, key) { return array.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294; } 

次に、例のサイクルの本体が大幅に削減されます。

 for (key in a) { if (arrayHasOwnIndex(a, key)) { console.log(a[key]); } } 

上記の検証コードは万能であり、すべての場合に適しています。 しかし、代わりに、正式なもので完全に正しいものではありませんが、より短いバージョンを使用できますが、それでもほとんどの場合に適しています:

 for (key in a) { if (a.hasOwnProperty(key) && String(parseInt(key, 10)) === key) { console.log(a[key]); } } 

4. for ...ループ(反復子の暗黙的な使用)


ES6はまだドラフトの状態ですが、JavaScriptでイテレーターを導入する必要があります。

反復子は、値のシーケンス(有限または無限)を取得する標準的な方法を定義するオブジェクトによって実装されるプロトコルです。
イテレータは、 next()メソッドが定義されているオブジェクトです。2つのプロパティを持つオブジェクトを返す引数のない関数です。
  1. doneboolean )-イテレータが反復可能なシーケンスの最後に到達した場合はtrue それ以外の場合はfalseです。
  2. value-反復子によって返される値を定義します。 doneプロパティがtrue場合、定義されない(存在しない)場合がありtrue

を含む多くの組み込みオブジェクト 実配列にはデフォルトの反復子があります。 実際の配列でイテレータを使用する最も簡単な方法は、新しいfor...of構文を使用するfor...of

for...ofの使用例:

 var val; var a = ["a", "b", "c"]; for (val of a) { console.log(val); } 

上記の例ではfor...ofループのfor...of Arrayオブジェクトの反復子を暗黙的に呼び出して、配列の各値を取得します。

5.イテレータの明示的な使用


イテレータは明示的に使用することもできますが、この場合、コードはfor...ofループに比べてはるかに複雑になります。 次のようになります。

 var a = ["a", "b", "c"]; var it = a.entries(); var entry; while (!(entry = it.next()).done) { console.log(entry.value[1]); } 

この例では、 Array.prototype.entriesメソッドは、配列の値を出力するために使用される反復子を返します。 各反復で、 entry.value[, ]形式の配列が含まれます。

II。 配列のようなオブジェクトの繰り返し


実際の配列に加えて、JavaScriptには配列のようなオブジェクトもあります 。 実際の配列では、 lengthプロパティと、配列の要素に対応する数字の形式の名前を持つプロパティを持つという事実によって関連付けられます。 例は、 NodeListコレクションのDOMと、関数/メソッド内でアクセス可能なarguments pseudo- NodeListです。

1.実際の配列を列挙する方法を使用する


少なくともすべてではありませんが、実際の配列を列挙する方法を使用して、配列のようなオブジェクトを列挙できます。

forおよびfor...inコンストラクトは、実際の配列とまったく同じ方法で配列のようなオブジェクトに適用できます。

forEachおよびその他のArray.prototypeメソッドArray.prototype 、配列のようなオブジェクトにも適用できます。 これを行うには、 Function.callまたはFunction.applyの呼び出しを使用します。

たとえば、 forEachNodeオブジェクトのchildNodesプロパティに適用する場合、これは次のように行われます。

 Array.prototype.forEach.call(node.childNodes, function(child) { //  -   child }); 

この手法を再利用するために、別の変数でArray.prototype.forEachメソッドへの参照を宣言し、それを短縮形として使用できます。

 // (,         ) var forEach = Array.prototype.forEach; // ... forEach.call(node.childNodes, function(child) { //  -   child }); 

配列のようなオブジェクトに反復子がある場合、それを明示的または暗黙的に使用して、実際の配列と同じ方法でオブジェクトを反復処理できます。

2.実配列に変換します


配列のようなオブジェクトを反復処理する別の非常に簡単な方法もあります。それを実際の配列に変換し、上記の方法を使用して実際の配列を反復処理します。 変換には、汎用メソッドArray.prototype.slice使用できます。これは、任意の配列のようなオブジェクトに適用できます。 以下の例に示すように、これは非常に簡単に行われます。

 var trueArray = Array.prototype.slice.call(arrayLikeObject, 0); 

たとえば、 NodeListコレクションを実際の配列に変換するには、次のようなものが必要です。

 var divs = Array.prototype.slice.call(document.querySelectorAll("div"), 0); 

更新ES6Array.prototype.slice代わりにES6rocktorbasowが指摘したように、より直感的なArray.fromメソッドを使用できます。

3.ランタイムオブジェクトに関する注意


Array.prototypeメソッドをArray.prototypeオブジェクト(DOMコレクションなど)に適用する場合、これらのメソッドの正しい動作がすべてのランタイム環境(ブラウザーを含む)で保証されるわけではないことに注意してください。 特定のランタイムでの特定のオブジェクトの動作、より正確には、このオブジェクトでHasProperty抽象操作がどのように実装されるかにHasPropertyます。 問題は、ES5標準自体が、この操作に関してオブジェクトの不正な動作の可能性を許可していることです( §8.6.2を参照)。

したがって、アプリケーションを使用する予定の各ランタイム(ブラウザー)でArray.prototypeメソッドの動作をテストすることが重要です。

Source: https://habr.com/ru/post/J247857/


All Articles