for...of

for...of 文は、反復可能オブジェクトをソースとした一連の値を処理するループを実行します。反復可能オブジェクトには、 たとえば組み込みの Array, String, TypedArray, Map, Set, NodeList(およびその他の DOM コレクション)、同様に arguments オブジェクトや、ジェネレーター関数から生成されるジェネレーター、ユーザー定義の反復可能オブジェクトなどがあります。

試してみましょう

構文

js
for (variable of iterable)
  statement
variable

反復処理の各回において、一連のデータから値を受け取ります。const, let, var の何れかで定義されたものか、代入のターゲットとなります(以前に宣言した変数や、オブジェクトプロパティなど)。

iterable

反復可能オブジェクトです。ループを実行する一連の値の元となるものです。

statement

反復処理のたびに実行される文です。variable を参照することができます。ブロック文を使用して、複数の文を実行することができます。

解説

for...of ループは、反復可能オブジェクトから取り出した値を 1 つずつ順次処理します。ループが値に対して行う各処理は反復処理と呼ばれ、ループは反復可能オブジェクトを反復処理すると言います。それぞれの反復処理では、現在のシーケンス値を参照する可能性のある文が実行されます。

for...of ループが反復可能オブジェクトを反復処理する場合、最初にその反復可能オブジェクトの [@@iterator]() メソッドが呼び出されます。これはイテレーターを返すので、その返されたイテレーターの next() メソッドを呼び出すことで、variable に代入される一連の値を生成することができます。

for...of ループは、イテレーターが完全に処理したときに終了します(イテレーターの next() メソッドは done: true を含むオブジェクトを返します)。また、通常の制御フローを変更するために制御フロー文を使用することもできます。break はループを抜けてループ本体の後の最初のステートメントに進み、continue は現在の反復処理の残りの文をスキップして次の反復処理に進みます。

for...of ループが早期に終了した場合(例えば、break 文に遭遇したり、エラーが発生した場合)、return() のメソッドが呼び出されクリーンアップ処理が行われます。

for...ofvariable 部分は、= 演算子の前に来ることができるものなら何でも受け入れることができます。const を使用して、ループ本体の中で再代入されない変数を宣言することができます(反復処理間では変更できます。2 つの別個の変数として扱われるからです)。そうでない場合は、let を使用してください。

js
const iterable = [10, 20, 30];

for (let value of iterable) {
  value += 1;
  console.log(value);
}
// 11
// 21
// 31

メモ: 反復処理ごとに新しい変数が作成されます。ループ本体内で変数を再代入しても、反復可能オブジェクト(この場合は配列)の元の値には影響しません。

分割代入や、for (x.y of iterable) などのオブジェクトプロパティを使用することもできます。

しかし、特別なルールにより、変数名として async を使用することは禁じられています。これは無効な構文です。

js
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 に対する反復処理

js
const iterable = [10, 20, 30];

for (const value of iterable) {
  console.log(value);
}
// 10
// 20
// 30

文字列に対する反復処理

文字列は Unicode コードポイントで反復処理します

js
const iterable = "boo";

for (const value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"

型付き配列に対する反復処理

js
const iterable = new Uint8Array([0x00, 0xff]);

for (const value of iterable) {
  console.log(value);
}
// 0
// 255

Map に対する反復処理

js
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 に対する反復処理

js
const iterable = new Set([1, 1, 2, 2, 3, 3]);

for (const value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

arguments オブジェクトに対する反復処理

arguments オブジェクトで反復処理をすると、ある JavaScript 関数にすべての引数を渡すことができます。

js
function foo() {
  for (const value of arguments) {
    console.log(value);
  }
}

foo(1, 2, 3);
// 1
// 2
// 3

NodeList に対する反復処理

次の例では read クラスを、<article> 要素の直接の子孫である段落に対して追加します。DOM の NodeList コレクションを反復処理すること実現します。

js
const articleParagraphs = document.querySelectorAll("article > p");
for (const paragraph of articleParagraphs) {
  paragraph.classList.add("read");
}

ユーザー定義の反復可能オブジェクトに対する反復処理

独自のイテレーターを返す @@iterator メソッドで、オブジェクトを反復処理します。

js
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

オブジェクトを @@iterator ジェネレーターメソッドで反復処理します。

js
const iterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  },
};

for (const value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

反復可能なイテレーター(this を返す [@@iterator]() メソッドを持つイテレーター)は、for...of などのイテレーターを想定した構文でイテレーターを使用可能にする、かなり一般的なテクニックです。

js
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

ジェネレーターに対する反復処理

js
function* source() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = source();

for (const value of generator) {
  console.log(value);
}
// 1
// 2
// 3

早期の脱出

最初のループで break ステートメントを実行すると、ループが早期に終了します。イテレーターはまだ完了していないので、2 つ目のループは最初のループが停止したところから継続されます。

js
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() メソッドを実装しており、ループが終了するとジェネレーター関数が早期復帰するように発生しています。このため、ジェネレータはループ間で再利用することができません。

js
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 ループを使用した場合の違いを示しています。

js
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

オブジェクト iterableobjCustom および arrCustom プロパティを継承しています。Object.prototype および Array.prototype の各プロパティを継承とプロトタイプチェーンで格納しているからです。

for...in ループは iterable オブジェクトの列挙可能なプロパティのみを出力します。配列の要素である 3, 5, 7"hello" は、列挙可能なプロパティではないため出力しません。これらは値です。配列の 添字arrCustomobjCustom と共に出力されます。なぜプロパティが反復処理に出てこないのかが分からない場合は、配列の反復処理と for...in にもっと詳しい説明があります。

2 番目のループは最初のものと似ていますが、Object.hasOwn() を使用して見つかった列挙可能なプロパティがオブジェクト自身のものであるか、すなわち継承したものでないかどうかをチェックしています。オブジェクト自身のプロパティである場合は、ログ出力します。0, 1, 2, foo は自身のプロパティであるため出力されます。arrCustomobjCustom は継承されたものであるために出力されません。

このループは、iterable反復可能オブジェクトとして定義している順序で を反復処理し、ログ出力します。オブジェクトの 要素 である 3, 5, 7 は表示されますが、オブジェクトの プロパティ は表示されません。

仕様書

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

ブラウザーの互換性

BCD tables only load in the browser

関連情報