whileループによるイテラブルのイテレーション同期イテレーションは、JavaScriptにおいて2つのエンティティグループを繋ぐ *プロトコル*(インターフェースとそれらを使用するためのルール)です。
**データソース:** 一方では、データは様々な形とサイズで存在します。JavaScriptの標準ライブラリには、線形データ構造であるArray、順序付きコレクションであるSet(要素は追加された時間で順序付けられる)、順序付き辞書であるMap(エントリは追加された時間で順序付けられる)、などが含まれます。ライブラリでは、ツリー状のデータ構造などが存在するかもしれません。
**データコンシューマ:** 他方では、入力に *逐次的に* アクセスするだけで済む構造体とアルゴリズムのクラス全体があります。一度に1つの値にアクセスし、すべての値がアクセスされるまで続けます。例としては、for-ofループや関数呼び出しへの展開(... を使用)などがあります。
イテレーションプロトコルは、Iterableインターフェースを介してこれら2つのグループを接続します。データソースは内容を「通して」逐次的に提供し、データコンシューマはそれを介して入力を受け取ります。
図 18 は、イテレーションの動作を示しています。データコンシューマはIterableインターフェースを使用し、データソースはそれを実装します。
JavaScriptにおけるインターフェースの実装方法
JavaScriptでは、オブジェクトが記述されているメソッドをすべて持っている場合、そのインターフェースを *実装* しているとみなされます。この章で言及されているインターフェースは、ECMAScript仕様にのみ存在します。
データのソースとコンシューマの両方がこの構成から利益を得ます。
新しいデータ構造を開発する場合、Iterableを実装するだけで、多くのツールをすぐに適用できます。
イテレーションを使用するコードを作成する場合、それは多くのデータソースで自動的に機能します。
2つの役割(インターフェースで記述される)がイテレーションの中核を形成します(図 19)。
これらは、イテレーションプロトコルのインターフェースに対する型定義です(TypeScriptの表記法)。
interface Iterable<T> {
[Symbol.iterator]() : Iterator<T>;
}
interface Iterator<T> {
next() : IteratorResult<T>;
}
interface IteratorResult<T> {
value: T;
done: boolean;
}インターフェースは次のように使用されます。
Symbol.iteratorであるメソッドを介して、Iterableにイテレータを要求します。Iteratorは、その.next()メソッドを介してイテレートされた値を返します。.valueはイテレートされた値です。.doneは、イテレーションの終端に達したかどうかを示します。最後のイテレートされた値の後ではtrue、それ以前ではfalseです。これはイテレーションプロトコルの使用方法の例です。
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 });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
イテレーションプロトコルの手動による使用方法を見てきましたが、比較的面倒です。しかし、このプロトコルは直接使用することを意図したものではなく、その上に構築されたより高度な言語構造を介して使用することを意図しています。このセクションでは、それがどのようなものかを示します。
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');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');次の組み込みデータソースはイテラブルです。
オブジェクトのプロパティをイテレートするには、Object.keys()やObject.entries()などのヘルパー関数が必要です。これは、プロパティがデータ構造のレベルとは独立した異なるレベルに存在するためです。
このセクションでは、同期イテレーションを使用する構造体をリストアップします。
配列パターンによるデストラクチャリング
const [x,y] = iterable;関数呼び出しと配列リテラルへの展開(... を使用)
func(...iterable);
const arr = [...iterable];for-ofループ
for (const x of iterable) { /*···*/ }yield*:
function* generatorFunction() {
yield* iterable;
}const obj = Object.fromEntries(iterableOverKeyValuePairs);const arr = Array.from(iterable);const m = new Map(iterableOverKeyValuePairs);
const wm = new WeakMap(iterableOverKeyValuePairs);const s = new Set(iterableOverElements);
const ws = new WeakSet(iterableOverElements);Promiseコンバイナ関数:Promise.all()など。
const promise1 = Promise.all(iterableOverPromises);
const promise2 = Promise.race(iterableOverPromises);
const promise3 = Promise.any(iterableOverPromises);
const promise4 = Promise.allSettled(iterableOverPromises); クイズ
クイズアプリを参照してください。