Iterator とは何か(まずイメージから)
Iterator(イテレータ)は、
「次の値をください」「もう終わりです」を順番に教えてくれる“値の自動販売機”のような仕組み です。
自販機にコインを入れると、1 本ずつジュースが出てきて、
最後には「もう売り切れです」と教えてくれますよね。
Iterator も同じで、
const iterator = 何かからイテレータを取得;
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
...
iterator.next(); // { value: undefined, done: true } もう終わり
JavaScriptという形で、「次の値」と「もう終わりかどうか」を返してくれます。
ここが重要です。
Iterator は「順番に値を取り出すための標準ルール」。
for…of や Set / Map / 配列の“裏側”で動いている共通の仕組みです。
イテレータの正体:next() メソッドを持つオブジェクト
next() が返すオブジェクトの形
イテレータは、最低限この形を守っているオブジェクトです。
const iterator = {
next() {
return {
value: 何かの値,
done: true または false,
};
},
};
JavaScriptnext() を呼ぶたびに、この { value, done } を返します。
done: falseの間は、valueに「まだ使える値」が入っている- 最後まで到達すると、
done: trueになり、「もう終わり」を示す
簡単な自前イテレータの例
1, 2, 3 まで数えるイテレータを自分で作ってみます。
function createCounter() {
let current = 1;
return {
next() {
if (current <= 3) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
},
};
}
const it = createCounter();
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 }
JavaScriptこれが Iterator の最も素の姿です。next() を呼ぶたびに、次の要素を返してくれます。
Iterable(イテラブル)と for…of の関係
「イテレータを返せるもの」が Iterable
ES6 では、
- 配列
- 文字列
- Map
- Set など
はすべて「イテラブル(Iterable)」です。
Iterable とは、
「自分専用のイテレータを返すメソッド [Symbol.iterator] を持っているもの」
のことです。
const array = [10, 20, 30];
const iterator = array[Symbol.iterator]();
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: 20, done: false }
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
JavaScriptfor…of は「裏でイテレータを使っている」
for...of は、実は裏側で [Symbol.iterator]() を呼び出しています。
const array = [10, 20, 30];
for (const value of array) {
console.log(value);
}
JavaScriptこれは、
const iterator = array[Symbol.iterator]();
while (true) {
const result = iterator.next();
if (result.done) break;
console.log(result.value);
}
JavaScriptを自動でやってくれているイメージです。
ここが重要です。
「for…of で回せるもの」は全部、「イテレータを返せるオブジェクト(Iterable)」。
Iterator は for…of の“エンジン”だと思ってください。
いろいろな組み込みオブジェクトのイテレータ
配列のイテレータ
const arr = ["A", "B"];
const it = arr[Symbol.iterator]();
console.log(it.next()); // { value: "A", done: false }
console.log(it.next()); // { value: "B", done: false }
console.log(it.next()); // { value: undefined, done: true }
JavaScript文字列のイテレータ
const str = "Hi";
const it = str[Symbol.iterator]();
console.log(it.next()); // { value: "H", done: false }
console.log(it.next()); // { value: "i", done: false }
console.log(it.next()); // { value: undefined, done: true }
JavaScriptMap / Set のイテレータ
const set = new Set([1, 2, 3]);
const setIt = set[Symbol.iterator]();
console.log(setIt.next()); // { value: 1, done: false } など
const map = new Map([
["a", 1],
["b", 2],
]);
const mapIt = map[Symbol.iterator]();
console.log(mapIt.next()); // { value: ["a", 1], done: false } など
JavaScriptMap の場合、for...of やイテレータで取り出される value は [key, value] のペアです。
自作オブジェクトを Iterable にしてみる
[Symbol.iterator] を実装してみる
例えば、「1 から n まで数えるオブジェクト」を自作してみます。
const counter = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
},
};
},
};
for (const num of counter) {
console.log(num);
}
// 1 2 3 4 5
JavaScriptポイントは [Symbol.iterator] が「イテレータを返す関数」になっていることです。
それだけで for...of の対象にできるようになります。
ここが重要です。
「自作クラスやオブジェクトを for…of で回したい」と思ったら、[Symbol.iterator] を実装する。
それがイテレータ対応の合図になります。
Iterator を理解すると見える世界(なぜ知る価値があるのか)
1. for…of / スプレッド構文 / 分割代入の裏側が分かる
例えば、
const arr = [1, 2, 3];
const copy = [...arr]; // スプレッド
const [a, b] = arr; // 配列の分割代入
JavaScriptこれらはすべて、「配列のイテレータ」を使って実現されています。
- スプレッド
...arr - 分割代入
[a, b] = arr for (const x of arr)
これらが「なんとなく便利な文法」ではなく、
「Iterable(イテレータを返せるもの)」に共通で使える仕組み だと分かると、
Map / Set などにも同じ発想を自然に広げられます。
2. 自作のデータ構造も「JS の一等市民」にできる
たとえば、独自のコレクション(例:ページネーションされた結果、ツリー構造のノードなど)を作ったとき、[Symbol.iterator] を実装すれば、for…of やスプレッド構文に対応させられます。
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
},
};
}
}
const r = new Range(1, 3);
console.log([...r]); // [1, 2, 3]
JavaScript自分で作ったクラスが、
配列と同じようにスプレッドできるのは、結構気持ちいい体験です。
3. ライブラリやフレームワークのコードが読めるようになる
React、Redux、各種ユーティリティライブラリなど、
モダンなライブラリの内部では Iterator / Iterable の仕組みが普通に使われています。
for...ofを自作のクラスに使っていたりSymbol.iteratorを定義していたり
そういうコードを見たときに、「これ何?」ではなく
「これはイテレータ対応だな」とすぐに分かるようになります。
ここが重要です。
Iterator を理解することは、「JS の文法レベル」というより、
「モダン JS の設計パターンの基礎」を押さえることに近い。
だからこそ、少し時間をかけてでも、一度きちんとイメージを掴んでおく価値があります。
まとめ
Iterator の本質は、
「next() で一つずつ値を返し、最後に done: true を返すオブジェクト」 です。
押さえておきたいポイントは次の通りです。
Iterator とは、next() メソッドを持ち、 { value, done } を返すオブジェクト
Iterable とは、[Symbol.iterator]() でイテレータを返せるオブジェクト(配列・文字列・Map・Set など)
for…of / スプレッド構文 / 分割代入は、裏でイテレータを使っている
自作オブジェクトに [Symbol.iterator] を実装すると、自分のクラスも for…of で回せるようになる
Iterator を理解すると、JS の「反復処理の共通ルール」が見えてきて、ライブラリのコードも読みやすくなる
最初の一歩としては、
配列や文字列に対して、
const it = 何か[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
...
JavaScriptを実際に打ってみてください。
いつも何気なく使っている配列や文字列の裏で、
「小さな自販機(Iterator)」が動いているイメージが、きっと掴めてくるはずです。
