「イテレーターとジェネレーター」をプログラミング初心者向けに噛み砕いて説明します。例をいっぱい入れて、実際に手を動かせるコード(ブラウザのコンソールでそのまま試せます)も載せます。まず要点をざっと押さえてから、具体例→実用例→練習問題、という流れで行きます。
ざっくり要点
- イテレーター(iterator):順番に値を取り出す「読み出し器」。
next()を呼ぶと{ value: X, done: false }のように返してくれるオブジェクトのこと。 - 反復可能(iterable)オブジェクト:
for...ofや展開(...)などで順に取り出せるもの。内部にSymbol.iteratorメソッドを持ち、呼ぶとイテレーターを返すオブジェクト。配列や文字列は反復可能。 - ジェネレーター(generator):簡単にイテレーターを作れる関数。
function*とyieldを使う。呼ぶとイテレーター(=ジェネレーターオブジェクト)を返す。next()でyieldのところまで実行を進め、値を取り出す。 - ジェネレーターは 「遅延評価」(値を必要になったときに作る)に便利。無限列や重い処理の逐次生成で威力を発揮。
1. イテレーターのしくみ
想像:本を一ページずつ読む → 「次のページをめくる」操作が next() に当たります。
イテレーターのルール(プロトコル)
next()を呼ぶと{ value: 取り出した値, done: 真偽 }を返す。done: falseはまだ要素がある。done: trueは終わり(valueは最後の値かundefined)。
簡単な手作りイテレーター(コード) — ブラウザのコンソールで実行してみてください:
function makeIterator(arr) {
let i = 0;
return {
next() {
if (i < arr.length) {
return { value: arr[i++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
const it = makeIterator(['a','b','c']);
console.log(it.next()); // {value: "a", done: false}
console.log(it.next()); // {value: "b", done: false}
console.log(it.next()); // {value: "c", done: false}
console.log(it.next()); // {value: undefined, done: true}
JavaScript2. 反復可能(iterable)オブジェクトと Symbol.iterator
配列は自動で反復可能([Symbol.iterator] を持つ)なので、for...of やスプレッドで使えます:
for (const x of ['x','y','z']) {
console.log(x); // x, y, z
}
JavaScriptarr[Symbol.iterator]() を呼すと配列のイテレーター(上と同じ役割)を手に入れられます。for...of は内部でこのイテレーターを使っています。
手動で Symbol.iterator を実装すると、自分のオブジェクトを for...of で使えるようにできます:
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (const n of range) console.log(n); // 1, 2, 3
JavaScript3. ジェネレーター(generator)を使うともっと簡単
ジェネレーターはイテレーターを簡単に書ける特別な関数です。function* と yield を使います。呼ぶとイテレーター(=ジェネレーターオブジェクト)を返します。
例:range をジェネレーターで実装
function* range(from, to) {
for (let i = from; i <= to; i++) {
yield i; // ここで値を外に渡して、次に呼ばれるまで関数は一時停止
}
}
const it = range(1,3);
console.log(it.next()); // {value:1, done:false}
console.log(it.next()); // {value:2, done:false}
console.log(it.next()); // {value:3, done:false}
console.log(it.next()); // {value:undefined, done:true}
// for...of でも使える
for (const x of range(4,6)) console.log(x); // 4,5,6
JavaScriptyield は「ここで値を返して一時停止する」命令。next() を呼ぶたびに次の yield まで実行が進みます。
4. ジェネレーターの便利機能(ちょっと上級)
yield*:他のイテレーター/ジェネレーターを委譲して全ての値を再配布できます(ネストを簡単に扱える)。- ジェネレーターオブジェクトの
next(value)に値を渡すと、直前のyield式の値として受け取れる(双方向のやり取り)。 return()を呼ぶとジェネレーターを途中で終了させることができる。throw()を使うとジェネレーター内に例外を送れる(エラーハンドリング用途)。
例(yield* の簡単な例):
function* inner() { yield 'A'; yield 'B'; }
function* outer() { yield 'start'; yield* inner(); yield 'end'; }
for (const x of outer()) console.log(x); // start, A, B, end
JavaScript5. どんな場面で使うと良い?(実用的ヒント)
- 大きなデータを逐次処理:全部一気に読み込まず、必要になった分だけ処理してメモリ節約。
- 無限列:(例:自然数、フィボナッチ)を作って必要なだけ取り出す。全部を配列にすると終わらない/重い。
- 非同期処理との組合せ:
asyncジェネレーター(async function*)はストリーミング的に非同期データを扱えます(別トピック)。
無限列の例(ジェネレーターで自然数列):
function* naturals() {
let i = 1;
while (true) yield i++;
}
const it = naturals();
console.log(it.next().value); // 1
console.log(it.next().value); // 2
// 必要なだけ繰り返せる(ただし止めどきは自分で制御)
JavaScript6. よくある間違い・つまずきポイント
yieldは値を返すが関数全体の戻り値ではない:yieldはその場で一回だけ返す。最後にジェネレーター全体を終えたときの戻り値はreturnで指定するか、done: trueのvalueを見る。forEachでは使えない:forEachは配列専用で内部でSymbol.iteratorを直接触らない場合があり、ジェネレーターのふるまいとは違う(for...ofを使う)。- 同期ジェネレーターと非同期(async)ジェネレーターを混同しない:非同期ストリームを扱うには
async function*とfor await...ofを学ぶ必要がある。
7 練習問題
- 数字の配列
[10,20,30]のSymbol.iterator()を取り出してnext()を手で3回呼んでみよう。何が返る? function* squares(n)を作って、1..n の平方(1,4,9,…)をyieldするジェネレーターを作ろう。for...ofで出力してみて。- ジェネレーターを使って、フィボナッチ数列(1,1,2,3,5,8…)を作り、最初の10個を出力してみよう。

