JavaScript for impatient programmers (ES2022版)
本書をサポートしてください:購入 または 寄付
(広告、ブロックしないでください。)

30 同期イテレーション



30.1 同期イテレーションとは何か?

同期イテレーションは、JavaScriptにおいて2つのエンティティグループを繋ぐ *プロトコル*(インターフェースとそれらを使用するためのルール)です。

イテレーションプロトコルは、Iterableインターフェースを介してこれら2つのグループを接続します。データソースは内容を「通して」逐次的に提供し、データコンシューマはそれを介して入力を受け取ります。

Figure 18: Data consumers such as the for-of loop use the interface Iterable. Data sources such as Arrays implement that interface.

18 は、イテレーションの動作を示しています。データコンシューマはIterableインターフェースを使用し、データソースはそれを実装します。

  JavaScriptにおけるインターフェースの実装方法

JavaScriptでは、オブジェクトが記述されているメソッドをすべて持っている場合、そのインターフェースを *実装* しているとみなされます。この章で言及されているインターフェースは、ECMAScript仕様にのみ存在します。

データのソースとコンシューマの両方がこの構成から利益を得ます。

30.2 コアイテレーション構造:イテラブルとイテレータ

2つの役割(インターフェースで記述される)がイテレーションの中核を形成します(図 19)。

Figure 19: Iteration has two main interfaces: Iterable and Iterator. The former has a method that returns the latter.

これらは、イテレーションプロトコルのインターフェースに対する型定義です(TypeScriptの表記法)。

interface Iterable<T> {
  [Symbol.iterator]() : Iterator<T>;
}

interface Iterator<T> {
  next() : IteratorResult<T>;
}

interface IteratorResult<T> {
  value: T;
  done: boolean;
}

インターフェースは次のように使用されます。

30.3 手動によるイテレーション

これはイテレーションプロトコルの使用方法の例です。

const iterable = ['a', 'b'];

// The iterable is a factory for iterators:
const iterator = iterable[Symbol.iterator]();

// Call .next() until .done is true:
assert.deepEqual(
  iterator.next(), { value: 'a', done: false });
assert.deepEqual(
  iterator.next(), { value: 'b', done: false });
assert.deepEqual(
  iterator.next(), { value: undefined, done: true });

30.3.1 whileループによるイテラブルのイテレーション

次のコードは、whileループを使用してイテラブルをイテレートする方法を示しています。

function logAll(iterable) {
  const iterator = iterable[Symbol.iterator]();
  while (true) {
    const {value, done} = iterator.next();
    if (done) break;
    console.log(value);
  }
}

logAll(['a', 'b']);
// Output:
// 'a'
// 'b'

  練習問題:同期イテレーションの手動による使用

exercises/sync-iteration-use/sync_iteration_manually_exrc.mjs

30.4 実践におけるイテレーション

イテレーションプロトコルの手動による使用方法を見てきましたが、比較的面倒です。しかし、このプロトコルは直接使用することを意図したものではなく、その上に構築されたより高度な言語構造を介して使用することを意図しています。このセクションでは、それがどのようなものかを示します。

30.4.1 配列のイテレーション

JavaScriptの配列はイテラブルです。これにより、for-ofループを使用できます。

const myArray = ['a', 'b', 'c'];

for (const x of myArray) {
  console.log(x);
}
// Output:
// 'a'
// 'b'
// 'c'

配列パターンによるデストラクチャリング(後で説明)も、内部でイテレーションを使用します。

const [first, second] = myArray;
assert.equal(first, 'a');
assert.equal(second, 'b');

30.4.2 Setのイテレーション

JavaScriptのSetデータ構造はイテラブルです。つまり、for-ofが機能します。

const mySet = new Set().add('a').add('b').add('c');

for (const x of mySet) {
  console.log(x);
}
// Output:
// 'a'
// 'b'
// 'c'

配列デストラクチャリングも同様です。

const [first, second] = mySet;
assert.equal(first, 'a');
assert.equal(second, 'b');

30.5 クイックリファレンス:同期イテレーション

30.5.1 イテラブルなデータソース

次の組み込みデータソースはイテラブルです。

オブジェクトのプロパティをイテレートするには、Object.keys()Object.entries()などのヘルパー関数が必要です。これは、プロパティがデータ構造のレベルとは独立した異なるレベルに存在するためです。

30.5.2 同期イテレーションを行う言語構造

このセクションでは、同期イテレーションを使用する構造体をリストアップします。

30.5.2.1 イテレーションを行う言語構造
30.5.2.2 イテラブルをデータ構造に変換する
30.5.2.3 その他

  クイズ

クイズアプリを参照してください。