for...of、スプレッド、分割代入の背後にあるプロトコルを習得し、ジェネレータを使って遅延評価のシーケンスを生成します。
このレッスンを Kodokon で開く反復を支配するのは 2 つの異なるプロトコルです。オブジェクトが Symbol.iterator キーの下にメソッドを公開しているとき、そのオブジェクトはイテラブル(iterable)です。そのメソッドはイテレータ(iterator)、つまり { value, done } を返す next() メソッドを持つオブジェクトを返さなければなりません。シーケンスを消費するものすべて(for...of、スプレッド演算子 ...、分割代入、Array.from、Promise.all)はこれらのプロトコルを経由します。したがって、どんなオブジェクトも互換にできるのです。
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { value: undefined, done: true };
}
};
}
};
console.log([...range]); // [1, 2, 3]イテレータを手書きするのは冗長です。ジェネレータ(function*)はイテレータを自動的に生成します。各 yield は実行を中断し、関数のローカルな状態を保持します。この遅延評価(lazy evaluation)により、消費側が要求する値の数を制限するかぎり、無限のシーケンスをモデル化できます。
function* naturals() {
let n = 0;
while (true) yield n++;
}
function* take(iterable, count) {
let index = 0;
for (const value of iterable) {
if (index >= count) return;
yield value;
index += 1;
}
}
console.log([...take(naturals(), 4)]); // [0, 1, 2, 3]ジェネレータのイテレータは return() と throw() も公開しています。見過ごされがちな仕様上の詳細があります。for...of ループを break、return、または例外で抜けると、エンジンは自動的に iterator.return() を呼び出します。ジェネレータでは、これによって保留中の finally ブロックが実行されます。ここはリソース(ファイル、コネクション、ロック)を解放するのに理想的な場所です。
function* readLines() {
try {
yield "line 1";
yield "line 2";
} finally {
console.log("cleanup");
}
}
for (const line of readLines()) {
console.log(line);
break; // triggers return(), hence the finally
}
// line 1, cleanuplength プロパティと数値インデックスを持つことnext() を持つオブジェクトを返す Symbol.iterator メソッドを公開することArray.prototype を継承することnext() メソッドを直接持つことfor...of ループを break で抜けると、何が起こりますか?throw() を呼び出すreturn() を呼び出し、それがジェネレータの finally ブロックを実行するnext() が呼ばれたときにのみ計算される