for...in
JavaScript で配列を使用しないように言われました。なぜだめですか?
27 に答える
その理由は、次の 1 つの構成要素にあります。
var a = []; // Create a new empty array.
a[5] = 5; // Perfectly legal JavaScript that resizes the array.
for (var i = 0; i < a.length; i++) {
// Iterate over numeric indexes from 0 to 5, as everyone expects.
console.log(a[i]);
}
/* Will display:
undefined
undefined
undefined
undefined
undefined
5
*/
他とはまったく異なる場合があります。
var a = [];
a[5] = 5;
for (var x in a) {
// Shows only the explicitly set index of "5", and ignores 0-4
console.log(x);
}
/* Will display:
5
*/
また、JavaScriptライブラリが次のようなことを行う可能性があることも考慮してください。これは、作成する配列に影響します。
// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;
// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
// Now foo is a part of EVERY array and
// will show up here as a value of 'x'.
console.log(x);
}
/* Will display:
0
1
2
3
4
foo
*/
ステートメント自体は「for-in
悪い習慣」ではありませんが、たとえば、配列や配列のようなオブジェクトを反復処理するために誤用される可能性があります。
for-in
ステートメントの目的は、オブジェクト プロパティを列挙することです。このステートメントは、プロトタイプ チェーンで上に移動し、継承されたプロパティも列挙しますが、これは望ましくない場合もあります。
また、反復の順序は仕様によって保証されていません。つまり、配列オブジェクトを「反復」したい場合、このステートメントでは、プロパティ (配列インデックス) が数値順にアクセスされることを確認できません。
たとえば、JScript (IE <= 8) では、プロパティが作成されたときに、配列オブジェクトでも列挙の順序が定義されます。
var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';
for (var p in array) {
//... p will be "2", "1" and "0" on IE
}
また、継承されたプロパティについて言えば、たとえば、Array.prototype
オブジェクトを拡張する場合 (MooTools のようないくつかのライブラリのように)、そのプロパティも列挙されます。
Array.prototype.last = function () { return this[this.length-1]; };
for (var p in []) { // an empty array
// last will be enumerated
}
前に述べたように、配列または配列のようなオブジェクトを反復処理するには、通常の/ループなどの順次ループを使用するのが最善です。for
while
オブジェクトの独自のプロパティ(継承されていないもの)のみを列挙する場合は、次のhasOwnProperty
メソッドを使用できます。
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
// prop is not inherited
}
}
Object.prototype
また、誰かが名前付きのプロパティhasOwnProperty
をオブジェクトに追加した場合に問題が発生しないように、から直接メソッドを呼び出すことを推奨する人もいます。
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// prop is not inherited
}
}
for..in
を使用して配列要素を反復処理してはならない理由は 3 つあります。
for..in
そうでない配列オブジェクトのすべての所有および継承されたプロパティをループしますDontEnum
。つまり、誰かが特定の配列オブジェクトにプロパティを追加した場合 (これには正当な理由があります - 私自身がそうしました)、または変更された場合Array.prototype
(他のスクリプトでうまく動作するはずのコードでは悪い習慣と見なされます)、これらのプロパティは同様に繰り返されます。継承されたプロパティはチェックすることで除外できますがhasOwnProperty()
、配列オブジェクト自体に設定されたプロパティには役立ちませんfor..in
要素の順序を保持することは保証されていません配列オブジェクトとそのプロトタイプチェーン全体のすべてのプロパティをたどる必要があり、プロパティの名前のみを取得する必要があるため、時間がかかります。つまり、値を取得するには、追加のルックアップが必要になります。
for...in は、配列自体ではなく、配列を保持するオブジェクトを列挙するためです。関数を配列プロトタイプ チェーンに追加すると、それも含まれます。いえ
Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
document.write(x + ' = ' + a[x]);
}
これは次のように書きます:
0 = フー 1 = バー myOwnFunction = function() { アラート(これ); }
また、プロトタイプ チェーンに何も追加されないという確証はないため、for ループを使用して配列を列挙します。
for(i=0,x=a.length;i<x;i++){
document.write(i + ' = ' + a[i]);
}
これは次のように書きます:
0 = フー 1 = バー
for…of
John Slegers がすでに気付いているように、2016 年 (ES6) の時点で、配列の反復に使用する可能性があります。
わかりやすくするために、この簡単なデモ コードを追加したいと思います。
Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";
console.log("for...of:");
var count = 0;
for (var item of arr) {
console.log(count + ":", item);
count++;
}
console.log("for...in:");
count = 0;
for (var item in arr) {
console.log(count + ":", item);
count++;
}
コンソールには次が表示されます。
for...of:
0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz
for...in:
0: 5
1: foo
言い換えると:
for...of
は 0 から 5 までカウントし、 も無視しArray.prototype.foo
ます。配列の値を示しています。for...in
のみをリストし5
、未定義の配列インデックスを無視しますが、 を追加しfoo
ます。配列のプロパティ名が表示されます。
簡単な答え:それだけの価値はありません。
長い答え:要素の順序と最適なパフォーマンスが必要ない場合でも、それだけの価値はありません。
長い答え:それだけの価値はありません...
- を使用
for (var property in array)
すると、オブジェクトarray
として繰り返され、オブジェクトのプロトタイプチェーンをトラバースし、最終的にはインデックスベースのループよりもパフォーマンスが低下します。for
for (... in ...)
予想どおり、オブジェクトのプロパティを順番に返すことは保証されていません。hasOwnProperty()
オブジェクトプロパティをフィルタリングするためのandcheckの使用!isNaN()
は追加のオーバーヘッドであり、実行速度がさらに遅くなり、そもそもそれを使用する主な理由が無効になります。つまり、形式がより簡潔になるためです。
これらの理由により、パフォーマンスと利便性の間の許容可能なトレードオフは存在しません。配列をオブジェクトとして処理し、配列のオブジェクトプロパティに対して操作を実行することが目的でない限り、実際には何のメリットもありません。
単独では、配列で for-in を使用しても問題はありません。for-in はオブジェクトのプロパティ名を反復処理し、「すぐに使える」配列の場合、プロパティは配列インデックスに対応します。length
( などの組み込みプロパティtoString
は反復には含まれません。)
ただし、コード (または使用しているフレームワーク) がカスタム プロパティを配列または配列プロトタイプに追加する場合、これらのプロパティは反復に含まれますが、これはおそらく望ましくありません。
Prototype などの一部の JS フレームワークは、Array プロトタイプを変更します。JQuery のような他のフレームワークはそうではないので、JQuery では安全に for-in を使用できます。
疑わしい場合は、おそらく for-in を使用しないでください。
配列を繰り返し処理する別の方法は、for ループを使用することです。
for (var ix=0;ix<arr.length;ix++) alert(ix);
ただし、これには別の問題があります。問題は、JavaScript 配列に「穴」ができることです。次のように定義した場合arr
:
var arr = ["hello"];
arr[100] = "goodbye";
次に、配列には 2 つの項目がありますが、長さは 101 です。for-in を使用すると 2 つのインデックスが生成されますが、for ループを使用すると 101 のインデックスが生成されます。99 の値はundefined
です。
他の回答で与えられた理由に加えて、ループはオブジェクトのプロパティの名前を反復処理するため、カウンター変数を使用して計算する必要がある場合は、「for...in」構造を使用したくない場合があります。は文字列です。
例えば、
for (var i=0; i<a.length; i++) {
document.write(i + ', ' + typeof i + ', ' + i+1);
}
書こう
0, number, 1
1, number, 2
...
一方、
for (var ii in a) {
document.write(i + ', ' + typeof i + ', ' + i+1);
}
書こう
0, string, 01
1, string, 11
...
もちろん、これを含めることで簡単に克服できます
ii = parseInt(ii);
ループ内ですが、最初の構造はより直接的です。
for
...in
がすべての列挙可能なプロパティ ( 「すべての配列要素」とは異なります!) をループするという事実は別として、http://www.ecma-international.org/publications/files/ECMA-ST/Ecma を参照してください。 -262.pdf、セクション 12.6.4 (第 5 版) または 13.7.5.15 (第 7 版):
プロパティを列挙するメカニズムと順序は...指定されていません...
(私のものを強調してください。)
つまり、ブラウザーが必要に応じて、挿入された順序でプロパティを通過できます。または番号順に。または字句順 (「30」は「4」の前に来る!すべてのオブジェクトキー、つまりすべての配列インデックスは実際には文字列であるため、完全に意味があることに注意してください)。オブジェクトをハッシュテーブルとして実装した場合、バケットごとにそれらを通過できます。または、そのいずれかを取り、「後方」を追加します。ブラウザは、各プロパティに 1 回だけアクセスする限り、ランダムに反復して ECMA-262 に準拠することさえできます。
実際には、ほとんどのブラウザーは現在、ほぼ同じ順序で反復することを好みます。しかし、彼らがしなければならないということは何もありません。これは実装固有のものであり、別の方法がはるかに効率的であることが判明した場合はいつでも変更される可能性があります。
いずれにせよ、for
...in
秩序を意味するものではありません。順序が気になる場合は、それを明示for
し、インデックスを使用して通常のループを使用してください。
主に2つの理由:
1
他の人が言ったように、配列にないキー、またはプロトタイプから継承されたキーを取得する可能性があります。たとえば、ライブラリがプロパティを Array または Object プロトタイプに追加するとします。
Array.prototype.someProperty = true
すべての配列の一部として取得します。
for(var item in [1,2,3]){
console.log(item) // will log 1,2,3 but also "someProperty"
}
hasOwnProperty メソッドでこれを解決できます。
var ary = [1,2,3];
for(var item in ary){
if(ary.hasOwnProperty(item)){
console.log(item) // will log only 1,2,3
}
}
ただし、これは、for-in ループを使用して任意のオブジェクトを反復処理する場合に当てはまります。
二
通常、配列内の項目の順序は重要ですが、for-in ループは必ずしも正しい順序で繰り返されるとは限りません。これは、配列をオブジェクトとして扱うためです。これは、JS で実装されている方法であり、配列として。これは小さなことのように思えますが、実際にはアプリケーションを台無しにする可能性があり、デバッグが困難です。
たとえば、追加することはあまりないと思います。場合によっては使用を避けるべき理由に関するトリプティクの回答またはCMSの回答。for...in
ただし、最新のブラウザーfor...in
では、使用できない場合に使用できる代替手段があることを付け加えたいと思いfor...in
ます。その代替手段は次のfor...of
とおりです。
for (var item of items) {
console.log(item);
}
ノート :
残念ながら、Internet Explorer のバージョンはサポートされていないためfor...of
( Edge 12+はサポートしています)、クライアント側の実稼働コードで使用できるようになるまで、もう少し待つ必要があります。ただし、サーバー側の JS コードで安全に使用できるはずです ( Node.jsを使用する場合)。
インデックスではなく、オブジェクト フィールドを列挙するためです。インデックス「長さ」で値を取得できますが、これが必要かどうかは疑問です。
問題for ... in ...
— これは、プログラマーが言語を本当に理解していない場合にのみ問題になります。これは実際にはバグでも何でもありません —オブジェクトのすべてのメンバー (すべての列挙可能なメンバーですが、それは今のところ詳細です) を反復処理するということです。配列のインデックス付きプロパティだけを反復処理する場合、意味の一貫性を維持する唯一の保証された方法は、整数インデックス (つまり、for (var i = 0; i < array.length; ++i)
スタイル ループ) を使用することです。
どのオブジェクトも、それに関連付けられた任意のプロパティを持つことができます。特に、追加のプロパティを配列インスタンスにロードすることについては何も問題はありません。したがって、インデックス付きの配列のようなプロパティのみを表示するコードは、整数インデックスに固執する必要があります。すべてのプロパティを確認する必要があることを完全に認識しているコードであれば、それも問題ありfor ... in
ません。
また、セマンティクスのために、for, in
が配列を処理する方法 (つまり、他の JavaScript オブジェクトと同じ) は、他の一般的な言語と一致していません。
// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"
// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x); //Output: "ABC"
// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x; //Output: "ABC"
// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x); //Output: "012"
for
/in
は、ハッシュテーブル(連想配列)と配列(非連想)の2種類の変数で機能します。
JavaScriptは、アイテムを通過する方法を自動的に決定します。したがって、配列が実際に非結合であることがわかっている場合はfor (var i=0; i<=arrayLen; i++)
、を使用して、自動検出の反復をスキップできます。
しかし、私の意見では、for
/を使用する方が良いと思いin
ます。その自動検出に必要なプロセスは非常に小さいです。
これに対する本当の答えは、ブラウザがJavaScriptコードをどのようにパーサー/解釈するかによって異なります。ブラウザ間で変わる可能性があります。
for
/を使わない他の目的は考えられませんin
。
//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
alert(arr[i]);
//Associative
var arr = {
item1 : 'a',
item2 : 'b',
item3 : 'c'
};
for (var i in arr)
alert(arr[i]);
他の問題に加えて、インデックスが整数ではなく文字列であるため、"for..in" 構文はおそらく遅くなります。
var a = ["a"]
for (var i in a)
alert(typeof i) // 'string'
for (var i = 0; i < a.length; i++)
alert(typeof i) // 'number'
注意しないと、プロトタイプチェーンの上位にあるオブジェクトに属するプロパティが繰り返されるためです。
を使用できますが、 hasOwnPropertyfor.. in
を使用して各プロパティを確認してください。
それは必ずしも悪いことではありません (あなたがしていることに基づいて) が、配列の場合、 に何かが追加されているとArray.prototype
、奇妙な結果が得られます。このループが 3 回実行されると予想される場所:
var arr = ['a','b','c'];
for (var key in arr) { ... }
と呼ばれる関数が に追加されている場合、ループhelpfulUtilityMethod
は4回実行されることになります。整数のみを期待していた場合は、おっと。Array
prototype
key
0
1
2
helpfulUtilityMethod
for(var x in y)
は、オブジェクトではなく、プロパティ リストに対してのみ使用する必要があります (上記で説明したように)。
JavaScript 要素は標準のオブジェクト プロパティとして保存されるため、通常の要素とすべての列挙可能なプロパティがリストされるため、for...in ループを使用して JavaScript 配列を反復処理することはお勧めできません。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collectionsから