for...of
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
for...of
文は、反復可能オブジェクトをソースとした一連の値を処理するループを実行します。反復可能オブジェクトには、
たとえば組み込みの Array
, String
, TypedArray
, Map
, Set
, NodeList
(およびその他の DOM コレクション)、同様に arguments
オブジェクトや、ジェネレーター関数から生成されるジェネレーター、ユーザー定義の反復可能オブジェクトなどがあります。
試してみましょう
構文
for (variable of iterable)
statement
解説
for...of
ループは、反復可能オブジェクトから取り出した値を 1 つずつ順次処理します。ループが値に対して行う各処理は反復処理と呼ばれ、ループは反復可能オブジェクトを反復処理すると言います。それぞれの反復処理では、現在のシーケンス値を参照する可能性のある文が実行されます。
for...of
ループが反復可能オブジェクトを反復処理する場合、最初にその反復可能オブジェクトの [Symbol.iterator]()
メソッドが呼び出されます。これはイテレーターを返すので、その返されたイテレーターの next()
メソッドを呼び出すことで、variable
に代入される一連の値を生成することができます。
for...of
ループは、イテレーターが完全に処理された時点で終了します(next()
の結果は、done: true
を持つオブジェクトです)。他のループ文と同様に、フロー制御文を statement
内部で使用することができます。
for...of
ループが早期に終了した場合(例えば、break
文に遭遇したり、エラーが発生した場合)、return()
のメソッドが呼び出されクリーンアップ処理が行われます。
for...of
の variable
部分は、=
演算子の前に来ることができるものなら何でも受け入れることができます。const
を使用して、ループ本体の中で再代入されない変数を宣言することができます(反復処理間では変更できます。2 つの別個の変数として扱われるからです)。そうでない場合は、let
を使用してください。
const iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
メモ: 反復処理ごとに新しい変数が作成されます。ループ本体内で変数を再代入しても、反復可能オブジェクト(この場合は配列)の元の値には影響しません。
分割代入を使用して複数のローカル変数に代入することもできますし、 for (x.y of iterable)
のようなプロパティアクセサーを使用して、オブジェクトプロパティに値を代入することもできます。
しかし、特別なルールにより、変数名として async
を使用することは禁じられています。これは無効な構文です。
let async;
for (async of [1, 2, 3]); // SyntaxError: The left-hand side of a for-of loop may not be 'async'.
これは、for
ループである有効なコード for (async of => {};)
との構文のあいまいさを避けるためです。
例
Array に対する反復処理
const iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value);
}
// 10
// 20
// 30
文字列に対する反復処理
文字列は Unicode コードポイント単位で反復処理します。
const iterable = "boo";
for (const value of iterable) {
console.log(value);
}
// "b"
// "o"
// "o"
型付き配列に対する反復処理
const iterable = new Uint8Array([0x00, 0xff]);
for (const value of iterable) {
console.log(value);
}
// 0
// 255
Map に対する反復処理
const iterable = new Map([
["a", 1],
["b", 2],
["c", 3],
]);
for (const entry of iterable) {
console.log(entry);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]
for (const [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3
Set に対する反復処理
const iterable = new Set([1, 1, 2, 2, 3, 3]);
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
arguments オブジェクトに対する反復処理
arguments
オブジェクトで反復処理をすると、ある JavaScript 関数にすべての引数を渡すことができます。
function foo() {
for (const value of arguments) {
console.log(value);
}
}
foo(1, 2, 3);
// 1
// 2
// 3
NodeList に対する反復処理
ユーザー定義の反復可能オブジェクトに対する反復処理
独自のイテレーターを返す [Symbol.iterator]()
メソッドで、オブジェクトを反復処理します。
const iterable = {
[Symbol.iterator]() {
let i = 1;
return {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
};
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
オブジェクトを [Symbol.iterator]()
ジェネレーターメソッドで反復処理します。
const iterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
反復可能なイテレーター(this
を返す [Symbol.iterator]()
メソッドを持つイテレーター)は、for...of
などのイテレーターを想定した構文でイテレーターを使用可能にする、かなり一般的なテクニックです。
let i = 1;
const iterator = {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
[Symbol.iterator]() {
return this;
},
};
for (const value of iterator) {
console.log(value);
}
// 1
// 2
// 3
ジェネレーターに対する反復処理
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
}
// 1
// 2
// 3
早期の脱出
最初のループで break
文を実行すると、ループが早期に終了します。イテレーターはまだ完了していないので、2 つ目のループは最初のループが停止したところから継続されます。
const source = [1, 2, 3];
const iterator = source[Symbol.iterator]();
for (const value of iterator) {
console.log(value);
if (value === 1) {
break;
}
console.log("この文字列はログ出力されません。");
}
// 1
// 同じイテレーターを使用する別のループは、
// 最後のループが終わったところをピックアップします。
for (const value of iterator) {
console.log(value);
}
// 2
// 3
// イテレーターを使い切りました。
// このループでは、反復処理は実行されません。
for (const value of iterator) {
console.log(value);
}
// [出力なし]
ジェネレーターは return()
メソッドを実装しており、ループが終了するとジェネレーター関数が早期復帰するように発生しています。このため、ジェネレーターはループをまたいで再利用することができません。
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
if (value === 1) {
break;
}
console.log("この文字列はログ出力されません。");
}
// 1
// イテレーターを使い切りました。
// このループでは、反復処理は実行されません。
for (const value of generator) {
console.log(value);
}
// [出力なし]
for...of と for...in との違い
for...in
および for...of
文は、両方とも何かに対する繰り返しです。これらの主な違いは、何に対する繰り返しなのかというところです。
for...in
文は、オブジェクトのすべての列挙可能なプロパティに対して、順序不定で繰り返し処理を行います。for...of
文は、反復可能なオブジェクトが定義した順序で値を反復処理します。
次の例では、Array
に対して for...of
ループと for...in
ループを使用した場合の違いを示しています。
Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};
const iterable = [3, 5, 7];
iterable.foo = "hello";
for (const i in iterable) {
console.log(i);
}
// "0", "1", "2", "foo", "arrCustom", "objCustom"
for (const i in iterable) {
if (Object.hasOwn(iterable, i)) {
console.log(i);
}
}
// "0" "1" "2" "foo"
for (const i of iterable) {
console.log(i);
}
// 3 5 7
オブジェクト iterable
は objCustom
および arrCustom
プロパティを継承しています。Object.prototype
および Array.prototype
の各プロパティを継承とプロトタイプチェーンで格納しているからです。
for...in
ループは iterable
オブジェクトの列挙可能なプロパティのみを出力します。配列の要素である 3
, 5
, 7
や "hello"
は、列挙可能なプロパティではないため出力しません。これらは値です。配列の 添字 が arrCustom
や objCustom
と共に出力されます。なぜプロパティが反復処理に出てこないのかが分からない場合は、配列の反復処理と for...in
にもっと詳しい説明があります。
2 番目のループは最初のものと似ていますが、Object.hasOwn()
を使用して見つかった列挙可能なプロパティがオブジェクト自身のものであるか、すなわち継承したものでないかどうかをチェックしています。オブジェクト自身のプロパティである場合は、ログ出力します。0
, 1
, 2
, foo
は自身のプロパティであるため出力されます。arrCustom
と objCustom
は継承されたものであるために出力されません。
for...of
ループは、iterable
が反復可能オブジェクトとして定義している順序で 値 を反復処理し、ログ出力します。オブジェクトの 要素 である 3
, 5
, 7
は表示されますが、オブジェクトの プロパティ は表示されません。
仕様書
Specification |
---|
ECMAScript Language Specification # sec-for-in-and-for-of-statements |
ブラウザーの互換性
BCD tables only load in the browser