配列反復で「for ... in」を使用することが悪い考えなのはなぜですか?


1823

for...inJavaScriptで配列を使用しないように言われました。何故なの?


45
誰かがあなたにそれを言った最近の質問を見ましたが、それらは配列のためだけのものでした。配列を反復することは悪い習慣と見なされますが、必ずしもオブジェクトのメンバーを反復することはできません。
mmurch 2010年

19
「for(var i = 0; i <hColl.length; i ++){}」などの「for」ループを使用した多くの回答は、「var i = hColl.length;と比較されます。一方(i--){} '後者の形式を使用できる場合は、かなり高速です。私はこれが正接であることを知っていますが、これを少し追加すると思いました。
Mark Schultheiss、2011年

2
@MarkSchultheissですが、これは逆の反復です。より速い順方向反復の別のバージョンはありますか?
ma11hew28

5
@Wynandは、var i = hCol1.length; for (i;i;i--;) {}キャッシュを使用してiを変更し、テストを簡素化します。-ブラウザ古い、とのより多くの違いforとは、while常に「i」のカウンターをキャッシュ-そしてもちろん否定は常に状況、および負しばらく収まらないobfuscate 一部の人のためのコードビットを。ノートvar i = 1000; for (i; i; i--) {}var b =1000 for (b; b--;) {}ここで、iが1に1000行くとbが0に999を行く-ブラウザより古い、よりしばらくはパフォーマンスを好む傾向があります。
Mark Schultheiss 2013年

9
あなたも賢いことができます。for(var i = 0, l = myArray.length; i < l; ++i) ...フォワードイテレーションで取得できる最速かつ最良の方法です。
Mathieu Amiot 2013

回答:


1557

その理由は、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
*/


146
歴史的には、一部のブラウザーは「長さ」、「toString」などを繰り返し処理していました。
ボビンス2009

398
グローバルを作成するの(var x in a)ではなく、使用することを忘れないでください(x in a)
Chris Morgan、

78
最初の問題はそれが悪い理由ではなく、意味論の違いだけです。2番目の問題は、組み込みのデータ型のプロトタイプを変更することは、for..inを変更するのではなく、悪いことである(同じことを行うライブラリ間の衝突に加えて)理由だと私には思われます。
スチュワート

86
@Stewart:JSのすべてのオブジェクトは関連付けられています。JS配列はオブジェクトなので、連想的ですが、それが目的ではありません。オブジェクトのキーを反復処理する場合は、を使用しますfor (var key in object)。ただし、配列の要素を反復処理する場合は、を使用しますfor(var i = 0; i < array.length; i += 1)
Martijn

42
最初の例では、0から4までの数値インデックスを反復処理すると誰もが予想するように、0から5まで反復すること期待しています。位置5に要素を追加すると、配列には6つの要素が含まれます(そのうちの5つは未定義)。
stivlo

393

for-in単独でのステートメントは、しかし、することができ、「悪い習慣」ではない誤使用する、例えば、反復配列または配列のようなオブジェクト上。

for-inステートメントの目的は、オブジェクトのプロパティを列挙することです。このステートメントはプロトタイプチェーンで上昇し、継承されたプロパティを列挙ますが、これは望ましくない場合あります。

また、反復の順序は仕様によって保証されていません。つまり、配列オブジェクトを「反復」したい場合、このステートメントでは、プロパティ(配列のインデックス)が番号順にアクセスされるかどうかを確認できません。

たとえば、JScript(IE <= 8)では、Arrayオブジェクトでも列挙の順序は、プロパティが作成されたときに定義されます。

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
}

配列または配列のようなオブジェクトを反復処理する前に述べたように、最善の方法は、単純な古い/ ループなどの順次ループを使用することです。forwhile

オブジェクトの継承されていないプロパティのみを列挙する場合は、次の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
  }
}

10
また、David Humphreyの投稿「JavaScriptでオブジェクトを繰り返し処理する」を参照してください-配列for..inは「通常の」ループよりもはるかに低速です。
Chris Morgan、

17
「hasOwnProperty」に関する最後のポイントについての質問:誰かがオブジェクトの「hasOwnProperty」をオーバーライドすると、問題が発生します。しかし、誰かが「Object.prototype.hasOwnProperty」をオーバーライドした場合にも同じ問題が発生しませんか?彼らはあなたを台無しにしていて、それはあなたの責任ではありませんか?
Scott Rippey、2011年

あなたfor..inは悪い習慣ではないと言いますが、それは誤用される可能性があります。継承されたプロパティを含むすべてのオブジェクトプロパティを実際に確認したいという、実際の良い例がありますか?
rjmunro 2013

4
@ScottRippey:そこに行きたい場合:youtube.com/watch
Nathan Wall

この答えで、私は値にアクセスできることがわかりましたfor (var p in array) { array[p]; }
イクイマン

117

for..in配列要素の反復処理に使用してはならない3つの理由があります。

  • for..inでない配列オブジェクトのすべての独自のプロパティと継承されたプロパティをループしDontEnumます。つまり、誰かが特定の配列オブジェクトにプロパティを追加した場合(これには正当な理由があります-私自身が行いました)、または変更した場合Array.prototype(他のスクリプトで適切に機能するはずのコードでは悪い習慣と見なされます)、これらのプロパティは同様に繰り返されます。継承されたプロパティは、をチェックすることで除外できますhasOwnProperty()が、配列オブジェクト自体に設定されたプロパティでは役立ちません

  • for..in 要素の順序を保持することは保証されていません

  • 配列オブジェクトとそのプロトタイプチェーンのすべてのプロパティを調べなければならず、プロパティの名前のみを取得するため、速度が遅くなります。つまり、値を取得するには、追加のルックアップが必要になります。


55

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 = foo
1 =バー
myOwnFunction = function(){alert(this); }

また、プロトタイプチェーンに何も追加されないことが確実ではないため、forループを使用して配列を列挙するだけです。

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

これは書きます:

0 = foo
1 =バー

16
配列オブジェクトであり、「配列を保持するオブジェクト」はありません。
RobG 2013年

41

単独では、配列でfor-inを使用しても何も問題はありません。for-inはオブジェクトのプロパティ名を反復処理します。「すぐに使える」配列の場合、プロパティは配列のインデックスに対応します。(同様にpropertes内蔵lengthtoStringなどの反復に含まれていません。)

ただし、コード(または使用しているフレームワーク)がカスタムプロパティを配列または配列プロトタイプに追加する場合、これらのプロパティは反復に含まれますが、これはおそらく必要なことではありません。

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です。


37

for…ofJohn 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...of0から5までカウントし、も無視しArray.prototype.fooます。配列の値が表示されます

  • for...in5未定義の配列インデックスを無視して、のみをリストしますが、を追加しfooます。配列のプロパティ名が表示されます。


32

短い答え:それだけの価値はありません。


より長い答え:連続した要素の順序と最適なパフォーマンスが要求されなくても、それだけの価値はありません。


長い答え:それだけの価値はありません...

  • を使用for (var property in array)するとarrayオブジェクトとして反復され、オブジェクトのプロトタイプチェーンをトラバースし、最終的にインデックスベースのforループよりもパフォーマンスが低下します。
  • for (... in ...) 当然のことながら、オブジェクトのプロパティが順番に返されるとは限りません。
  • オブジェクトプロパティをフィルタリングするために使用hasOwnProperty()および!isNaN()チェックすることは、オーバーヘッドをさらに増加させ、実行速度をさらに低下させ、そもそもそれを使用する主な理由、つまり形式がより簡潔になるという理由を打ち消します。

これらの理由から、パフォーマンスと利便性の間の許容できるトレードオフは存在しません。目的が配列をオブジェクトとして処理し、配列のオブジェクトプロパティに対して操作を実行することでない限り、実際には何のメリットもありません。


31

他の回答で与えられた理由に加えて、ループがオブジェクトのプロパティの名前を繰り返し処理するため、カウンター変数を使用して計算を行う必要がある場合は、「...」構造を使用したくない場合があります。文字列です。

例えば、

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);

ループでは、最初の構造はより直接的です。


6
本当に整数が必要+parseIntないか、無効な文字を無視しない限り、代わりに接頭辞を使用できます。
Konrad Borowski

また、使用parseInt()はお勧めしません。試してみてくださいparseInt("025");、それがされます失敗。
Derek款會功夫

6
@Derek読取會功夫-あなたは間違いなく使用できますparseInt。問題は、基数を含めないと、古いブラウザが数値を解釈しようとする可能性があることです(したがって、025は8進数になります)。これはECMAScript 5で修正されましたが、「0x」で始まる数値でも発生します(数値を16進数として解釈します)。安全のために、そのような数を指定する基数を使用してparseInt("025", 10)指定の10を底こと-
IAmTimCorey

23

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して、インデックス付きの通常のループを使用してください。


18

主に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で実装されている方法であり、配列として。これは小さなことのように見えますが、実際にアプリケーションを台無しにする可能性があり、デバッグが困難です。


2
Object.keys(a).forEach( function(item) { console.log(item) } )プロトタイプから継承したキーではなく、独自のプロパティキーの配列を反復処理します。
Qwerty

2
真ですが、for-inループのように、必ずしも正しいインデックス順ではありません。また、ES5をサポートしていない古いブラウザでは機能しません。
Lior 2014年

array.forEachスクリプトに特定のコードを挿入することで、これらのブラウザーを教えることができます。参照ポリフィルdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
QWERTY配列の

もちろん、しかし、あなたはプロトタイプを操作している、そしてそれは常に良い考えではない...そしてそれでも、あなたは注文の問題を抱えている...
Lior

そしてもちろん、理由3番:スパース配列。
より良いオリーブ

16

インデックスではなくオブジェクトフィールドを列挙するためです。インデックス「長さ」で値を取得できますが、これが欲しいとは思いません。


それを行うための最良の方法は何ですか?
lYriCAlsSH 2009

3
for(var i = 0; i <arr.length; i ++){}
vava

3
Firefox 3では、arr.forEachまたはfor(var [i、v] in Iterator(arr)){}のいずれかを使用することもできますが、forEachメソッドを自分で作成することはできますが、IEでは機能しません。
vava

事実上、すべてのライブラリには、独自の方法もあります。
vava

5
この答えは間違っています。「長さ」はfor-in反復には含まれません。含まれているのは、自分で追加するプロパティのみです。
JacquesB 2009

16

たとえば、追加することはあまりないと思います。場合によっては使用を避けるべき理由に関するトリプティクの回答またはCMSの回答for...in

ただし、最近のブラウザでは、for...in使用for...inできない場合に使用できる代替手段があることを付け加えておきます。その代替はfor...of

for (var item of items) {
    console.log(item);
}

注意 :

残念ながら、Internet Explorer のどのバージョンもサポートしていませんfor...ofEdge 12+はサポートしています)。そのため、クライアント側の製品コードで使用できるようになるまでもう少し待つ必要があります。ただし、サーバー側のJSコードで安全に使用できます(Node.jsを使用する場合)。


@georgeawg for-ofではなくfor-in、という意味ですか?
ᆼᆺᆼ

15

この問題はfor ... in ...、プログラマーが言語を本当に理解していない場合にのみ問題になります。これは実際にはバグではなく、オブジェクトのすべてのメンバーを反復処理するということです(すべての列挙可能なメンバーですが、今のところ詳細です)。配列のインデックス付きプロパティのみを反復処理する場合、意味的に一貫性を保つ唯一の保証された方法は、整数インデックス(つまり、for (var i = 0; i < array.length; ++i)スタイルループ)を使用することです。

どのオブジェクトにも任意のプロパティを関連付けることができます。特に配列インスタンスに追加のプロパティをロードすることについては何もひどいことはありません。したがって、インデックス付きの配列のようなプロパティのみを表示したいコードは、整数インデックスに固執する必要があります。何for ... inが何であるかを完全に認識しており、実際にすべてのプロパティを表示する必要があるコードは、それでも問題ありません。


いい説明先のとがった。ちょっと興味があるんだけど。for in通常のforループと比較して、multiplyプロパティの下のオブジェクト内にある配列があり、そうした場合、それらの配列は反復されますか?(本質的に、パフォーマンスが遅い、そうでしょう?)
NiCk Newman 2015

2
@NiCkNewmanあなたが後に参照するだけでなく、オブジェクトinfor ... inのループになるだけ
先のとがった

そうですか。私のメインゲームオブジェクト内にオブジェクトと配列があり、インデックスの通常のforループよりもイニングの方が苦痛かどうか疑問に思っていました。
NiCkニューマン2015

@NiCkNewmanこの質問全体のテーマはfor ... in、配列では使用しないことです。そうしない理由はたくさんあります。これは、「壊れないことを確認する」問題ほどのパフォーマンスの問題ではありません。
2015

まあ、私のオブジェクトは技術的に配列に格納されているので、次のような心配[{a:'hey',b:'hi'},{a:'hey',b:'hi'}]がありました。しかし、そうです、私は理解しています。
NiCkニューマン2015

9

また、セマンティクスのため、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"

9

TL&DR:for in配列でループを使用することは悪いことではなく、実際にはまったく逆です。

配列で正しくfor in使用すれば、ループはJSの逸品だと思います。ソフトウェアを完全に制御し、自分が何をしているかを理解していることが求められます。前述の欠点を見て、それらを1つずつ反証しましょう。

  1. 継承されたプロパティもループします。最初に、への拡張はをArray.prototype使用して行われている必要がObject.defineProperty()あり、そのenumerable記述子はに設定されてfalseいる必要があります。そうしていないライブラリは使用しないでください。
  2. 継承チェーンに追加したプロパティは、後でカウントされます。Classによる、Object.setPrototypeOfまたはClass による配列のサブクラス化extendObject.defineProperty()デフォルトwritableenumerableconfigurableプロパティ記述子をに設定するを再び使用する必要がありますfalse。ここで配列のサブクラス化の例を見てみましょう...

function Stack(...a){
  var stack = new Array(...a);
  Object.setPrototypeOf(stack, Stack.prototype);
  return stack;
}
Stack.prototype = Object.create(Array.prototype);                                 // now stack has full access to array methods.
Object.defineProperty(Stack.prototype,"constructor",{value:Stack});               // now Stack is a proper constructor
Object.defineProperty(Stack.prototype,"peak",{value: function(){                  // add Stack "only" methods to the Stack.prototype.
                                                       return this[this.length-1];
                                                     }
                                             });
var s = new Stack(1,2,3,4,1);
console.log(s.peak());
s[s.length] = 7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);

for(var i in s) console.log(s[i]);

あなたが見る..だからfor inループがあなたのコードを気にするので、今安全です。

  1. for inループは遅いです:地獄いいえ。時々必要なスパース配列をループする場合は、これが最も高速な反復方法です。これは、知っておくべき最も重要なパフォーマンストリックの1つです。例を見てみましょう。スパース配列をループします。

var a = [];
a[0] = "zero";
a[10000000] = "ten million";
console.time("for loop on array a:");
for(var i=0; i < a.length; i++) a[i] && console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");
for(var i in a) a[i] && console.log(a[i]);
console.timeEnd("for in loop on array a:");


@Ravi Shanker Reddy Goodベンチマークが設定されました。私の回答で述べたように、for inループが他の配列よりも優れており(配列がスパースの場合)、サイズが大きくなればなるほど多くなります。したがって、ランダムなインデックスからarrランダムに選択された50アイテムのみで、サイズが〜10000のスパース配列のベンチテストを再配置しました[42,"test",{t:1},null, void 0]。すぐに違いがわかります。->>こちらで確認してください<<-
Redu

8

他の問題に加えて、インデックスは整数ではなく文字列であるため、「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'

おそらく大した問題ではありません。配列要素は、配列ベースまたは配列のようなオブジェクトのプロパティであり、すべてのオブジェクトプロパティには文字列キーがあります。JSエンジンが何らかの方法で最適化しない限り、数値を使用したとしても、ルックアップ用の文字列に変換されてしまいます。
cHao

パフォーマンスの問題に関係なく、JavaScriptを初めて使用する場合var i in aは、インデックスを使用して整数であると想定すると、次のようなa[i+offset] = <value>ことを行うと、値が完全に間違った場所に配置されます。( "1" + 1 == "11")。
szmoore 2014

8

重要な側面はfor...in列挙可能な プロパティ属性がtrueに設定されているオブジェクトに含まれるプロパティのみを反復することです。したがって、使用してオブジェクトを反復しようとするとfor...in、列挙可能なプロパティ属性がfalseの場合、任意のプロパティが失われる可能性があります。特定の要素が列挙されないように、通常のArrayオブジェクトの列挙可能なプロパティ属性を変更することは十分に可能です。一般に、プロパティ属性はオブジェクト内の関数プロパティに適用される傾向があります。

プロパティの列挙可能なプロパティ属性の値は、次の方法で確認できます。

myobject.propertyIsEnumerable('myproperty')

または、4つのプロパティ属性すべてを取得するには:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

これはECMAScript 5で利用可能な機能です-以前のバージョンでは、列挙可能なプロパティ属性の値を変更することはできませんでした(常にtrueに設定されていました)。


8

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]);

プロトタイプオブジェクトを使用している場合を除き、true。;)以下
Ricardo

その理由ArrayObjectそうです
無料コンサルティング

2
for ... inオブジェクトを操作します。自動検出のようなものはありません。
より良いオリーブ

7

注意しないと、プロトタイプチェーンの上のオブジェクトに属するプロパティを反復処理するためです。

を使用できます。for.. in必ずhasOwnPropertyで各プロパティを確認してください。


2
十分ではありません-任意の名前付きプロパティを配列インスタンスに追加しても問題ありません。それらはチェックtrueからテストされhasOwnProperty()ます。
先のとがった

良い点、ありがとう。私はそれを自分でArrayに実行するのに十分な馬鹿げたことはありませんでした。
JAL

1
@Pointy私はこれをテストしていませんがisNaN、各プロパティ名のチェックを使用することでこれを克服できる可能性があります。
WynandB 2013年

1
@Wynandの興味深いアイデア。ただし、単純な数値インデックスを使用して反復することが非常に簡単であるのに、なぜそれが問題になる価値があるのか​​、実際にはわかりません。
先のとがっ

@WynandBバンプのため申し訳ありませんが、修正が正しいと感じています:isNaN変数が特別な値NaNであるかどうかをチェックするためのものです、「数値以外のもの」をチェックするために使用することはできません(通常のそのためのtypeof)。
doldt 2015

6

これは必ずしも悪いことではありません(何をしているのかによります)が、配列の場合、に何かが追加されているとArray.prototype、奇妙な結果が得られます。このループが3回実行されると予想される場所:

var arr = ['a','b','c'];
for (var key in arr) { ... }

呼び出された関数は、場合helpfulUtilityMethodに追加されましたArrayさんprototype:、その後、あなたのループを4回実行し終わるだろうkeyだろう012、とhelpfulUtilityMethod。整数しか期待していない場合は、おっと。


6

for(var x in y)(上記で説明したように)オブジェクトではなく、プロパティリストでのみ使用する必要があります。


13
SOに関する注意事項-コメントは常にページ上の順序を変更するため、「上記」はありません。したがって、私たちはあなたがどのコメントを意味するのか本当にわかりません。この理由から、「x人のコメントで」と言うのは良いことです。
JAL

@JAL ...または回答にパーマリンクを追加します。
WynandB 2013年

5

for...in配列にループを使用することは間違っていませんが、なぜ誰かがあなたにそう言ったのかは推測できます:

1.)配列に対してその目的を持つより高次の関数またはメソッドが既に存在しますが、「forEach」と呼ばれるより多くの機能性とより簡潔な構文を持っています。 Array.prototype.forEach(function(element, index, array) {} );

2.)配列は、常に長さを持っていますが、for...inforEachある任意の値のための関数を実行していません'undefined'だけで定義された値を持つインデックスに対して、。したがって、値を1つだけ割り当てる場合、これらのループは関数を1回だけ実行しますが、配列が列挙されるため、定義された値を持つ最高のインデックスまでの長さが常にありますが、これらの長さはこれらを使用すると気付かれない可能性がありますループ。

3.)標準のforループは、パラメーターで定義した回数だけ関数を実行します。配列には番号が付けられているため、関数を実行する回数を定義する方が理にかなっています。他のループとは異なり、forループは、値が定義されているかどうかに関係なく、配列内のすべてのインデックスに対して関数を実行できます。

本質的に、どのループでも使用できますが、ループの動作を正確に覚えておく必要があります。さまざまなループが繰り返される条件、それらの個別の機能を理解し、それらがさまざまなシナリオに多かれ少なかれ適切であることを理解します。

また、一般的にループforEachよりもメソッドを使用する方が良い方法と考えられるかもしれません。for...in書く方が簡単で機能も多いので、このメソッドと標準のみを使用する習慣を身につけたいと思うかもしれませんが、コール。

最初の2つのループはconsole.logステートメントを1回だけ実行しますが、標準のforループは指定された回数だけ関数を実行します(この場合は、array.length = 6)。

var arr = [];
arr[5] = 'F';

for (var index in arr) {
console.log(index);
console.log(arr[index]);
console.log(arr)
}
// 5
// 'F'
// => (6) [undefined x 5, 6]

arr.forEach(function(element, index, arr) {
console.log(index);
console.log(element);
console.log(arr);
});
// 5
// 'F'
// => Array (6) [undefined x 5, 6]

for (var index = 0; index < arr.length; index++) {
console.log(index);
console.log(arr[index]);
console.log(arr);
};
// 0
// undefined
// => Array (6) [undefined x 5, 6]

// 1
// undefined
// => Array (6) [undefined x 5, 6]

// 2
// undefined
// => Array (6) [undefined x 5, 6]

// 3
// undefined
// => Array (6) [undefined x 5, 6]

// 4
// undefined
// => Array (6) [undefined x 5, 6]

// 5
// 'F'
// => Array (6) [undefined x 5, 6]

4

これが(通常)悪い習慣である理由は次のとおりです。

  1. for...inループは、独自の列挙可能なプロパティプロトタイプの列挙可能なプロパティすべてを反復します。通常、配列の反復では、配列自体を反復するだけです。そして、あなた自身が配列に何も追加しなくても、ライブラリまたはフレームワークが何かを追加する可能性があります。

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...inループは特定の反復順序を保証しません。最近では、ほとんどの最新ブラウザーで順序が見られますが、100%の保証はまだありません。
  2. for...inループはundefined配列要素、つまりまだ割り当てられていない配列要素を無視します。

::

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}


2

for ... inは、JavaScriptでオブジェクトを操作するときに役立ちますが、配列に対しては役立ちませんが、間違った方法であるとは言えませんが、お勧めできません以下のfor ... inループを使用した例をご覧ください。

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

さて、今それを配列でやってみましょう:

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

結果は同じです...

しかし、何かを試してみましょう。何かをArrayにプロトタイプ化してみましょう...

Array.prototype.someoneelse = "someoneelse";

次に、新しいArray();を作成します。

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

誰かが表示されます !!! ...この場合、実際には新しいArrayオブジェクトをループします!

それが、慎重にfor..inを使用する必要がある理由の1つですが、常にそうであるとは限りません...


2

for ... inループは常にキーを列挙します。オブジェクトのプロパティキーは、配列のインデックス付きプロパティでさえ、常にStringです。

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123


0

特にこの問題によって対処されていないが、私がいないため、これまで使用することは非常に良い理由があることを追加します...としてNodeList(1から得られるだろうとしてquerySelectorAllではなく、すべての返された要素を見ていないとして、コールNodeListプロパティのみを反復処理します。

単一の結果の場合、私は得ました:

var nodes = document.querySelectorAll(selector);
nodes
 NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

for (node in nodes) node.href = newLink;が失敗した理由が説明されました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.