for...in

for...inは、キーが文字列であるオブジェクトの列挙可能プロパティすべてに対して、継承された列挙可能プロパティも含めて反復処理を行います (Symbol がキーになったものは無視します)。

試してみましょう

構文

for (variable in object)
  文
variable

反復するごとに、 variable に異なるプロパティ名が代入されます。

object

このオブジェクトの列挙可能プロパティに対して反復処理がされます。

解説

for...in ループは、列挙可能なシンボル以外のプロパティに対してのみ反復処理を行います。 ArrayObject のような組込みコンストラクターから生成したオブジェクトは、列挙可能でないプロパティを Objet.prototypeString.prototype から、例えば StringindexOf() メソッドや ObjecttoString() メソッドを継承しています。このループは、対象オブジェクト自身とそのオブジェクトがプロトタイプから継承しているすべての列挙可能なプロパティを反復します (プロトタイプチェーンで対象オブジェクトに近いプロパティは、親プロトタイプのプロパティを上書きします)。

プロパティの変更や削除

for...in ループは、任意の順序でオブジェクトのプロパティに対して反復します (なぜ繰り返しの見かけの順序に依存できないのかについては、詳細は delete 演算子を見てください)。

もしプロパティがある反復で修正されて、その後に訪問されたなら、ループにより公開される値は後の時点での値となります。訪問される前に削除されたプロパティは、それから後には訪問されません。オブジェクトに対する反復が起きている中でそのオブジェクトに追加されたプロパティは、訪問されるかもしれませんし反復から省略されるかもしれません。

一般的に、現在訪問しているプロパティ以外のものに関しては、反復の間はオブジェクトにプロパティを追加、修正、または削除しないのが一番です。追加したプロパティが訪問されるか、(現在のもの以外の)修正したプロパティが修正される前または後に訪問されるか、または削除したプロパティが削除される前に訪問されるかといったことには、何の保証もありません。

配列の繰り返しと for...in

メモ: for...in はインデックスの順序が重要となる 配列 の繰り返しには使うべきではありません。

配列のインデックスは単に整数値の名前で列挙できるプロパティであり、そうでないと一般的なオブジェクトのプロパティとして一意になりません。 for...in は特定の順序で並べられる保証はありません。 for...in ループ文はすべての列挙できるプロパティを返し、その中には非整数型やそれを引き継いだインデックス名があります。

繰り返しの順序が実装依存なため、配列の繰り返しは要素を一貫した順番で参照することになるとは限りません。このため、アクセスの順番が大事となる配列を繰り返す時には、数値のインデックスでの for ループ (か Array.prototype.forEach()for...of ループ) を使った方が良いです。

独自のプロパティだけで繰り返す

オブジェクトに付属するプロパティだけを考えればよい場合、 getOwnPropertyNames() を使うか、 hasOwnProperty() を実行してチェックします(propertyIsEnumerable も使用できます)。または、外部のコードインターフェイスをまったく知らない場合は、チェックメソッドを備えた組み込みの prototypes を継承できます。

for...in を使用する理由

for...in はオブジェクトのプロパティを反復するために作られたものであり、配列での使用は推奨されず、 Array.prototype.forEach()for...of などの選択肢があるわけですが、それでは for...in を使用する場面は何なのでしょうか?

最も具体的な使い方はデバッグ目的であるかもしれません。これは、オブジェクトのプロパティを (コンソールに出力するなどして) 簡単にチェックする方法になります。データを格納するには配列の方が実用的な場合が多いですが、データを扱うにはキーと値のペアが好まれる状況では (プロパティが "キー" として機能します)、それらのキーが特定の値を保持しているかどうかをチェックしたい場合があるかもしれません。

for...in の使用

以下の for...in ループは、オブジェクトの列挙可能なシンボルではないプロパティをすべて反復し、そのプロパティ名と値を文字列で記録します。

var obj = {a: 1, b: 2, c: 3};

for (const prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

自身のプロパティの反復処理

次の関数では hasOwnProperty(): の使い方を例示しています。継承されたプロパティは表示されません。

var triangle = {a: 1, b: 2, c: 3};

function ColoredTriangle() {
  this.color = 'red';
}

ColoredTriangle.prototype = triangle;

var obj = new ColoredTriangle();

for (const prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    console.log(`obj.${prop} = ${obj[prop]}`);
  }
}

// Output:
// "obj.color = red"

仕様書

Specification
ECMAScript Language Specification
# sec-for-in-and-for-of-statements

ブラウザーの互換性

BCD tables only load in the browser

厳格モードにおける初期化式の互換性について

Firefox 40 より前では、for...in ループ内で初期化式 (i=0) が使用可能でした。

var obj = {a: 1, b: 2, c: 3};
for (var i = 0 in obj) {
  console.log(obj[i]);
}
// 1
// 2
// 3

この標準外の動作はバージョン 40 以降では無視され、厳格モードでの SyntaxError ("for-in loop head declarations may not have initializers") エラーが表示されます (bug 748550 および bug 1164741)。

v8 (Chrome), Chakra (IE/Edge), JSC (WebKit/Safari) といった他のエンジンも同様に非標準のふるまいを削除するよう開発しています。

関連情報