1. Symbol.iterator とは?
• 定義
Symbol.iterator は、JavaScript に最初から用意されている「特別な印(シンボル)」のひとつです。
• 役割
そのオブジェクトが「順番に中身を取り出せるもの(反復可能=iterable)」だと教えるための合図になります。
→ これがあると for…of や …(スプレッド構文)で中身を順番に取り出せる。
• 戻り値
Symbol.iterator に対応するメソッドは「イテレーター」と呼ばれる専用の仕組みを返します。
イティセル/コード専門官つまり、Symbol.iterator を実装すれば、そのオブジェクトは for…of やスプレッド構文で展開できるようになります。



要するに、順番に値を取り出せるようにして、次はこれだよ、ここで終わりだよ、と教えてくれる仕組みです。
イテレーターとは、オブジェクトの要素を「順番に1つずつ取り出すための仕組み」を持ったオブジェクトのことです。
2. 代表的な反復可能オブジェクト





Symbol.iterator に対応している代表的なオブジェクトは、次のように反復処理が可能です。つまり、これらは「順番に中身を取り出せる仲間たち」です。
• Array
• String
• Map
• Set
• TypedArray
• arguments オブジェクト
• DOM コレクション(NodeList など)



例えば、arr と Symbol.iterator を組み合わせて typeof を実行すると “function” が返ってきます。これは「関数」だからです。
一方、arr だけで typeof を実行すると “object” になります。配列はオブジェクトの一種だからです。
つまり、Symbol.iterator を使わなければただの配列ですが、Symbol.iterator を参照すると「中身を順番に取り出す工場(関数)」が現れるイメージになります。
const arr = [1, 2, 3];
console.log(typeof arr[Symbol.iterator]); 


“function”
実行結果
"function"


arr の中身は [1, 2, 3] です。
arr[Symbol.iterator]() を呼ぶと「配列を順番に取り出す仕組み(イテレーター)」が作られます。
console.log(iterator.next()); の結果は { value: 1, done: false } になります。
• value: 1 → 配列の最初の要素が返る
• done: false → まだ次の要素が残っている
要するに、これは「最初の呼び出しだから 1 が返ってきた。でも配列にはまだ続きがあるよ」というサインです。
わかりやすくいうと
• value: 1 → 「 arr=[1, 2, 3] の内今取り出した値は 1 です」
• done: false → 「まだ次の値(2, 3)が残っています」
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next());


{ value: 1, done: false }
実行結果
{ value: 1, done: false }3. イテレーターの仕組み





イテレーターは next() メソッドを持つオブジェクトで、呼び出すたびに { value, done } を返します。
簡単に言うと、next() を呼ぶごとに「次の値」と「まだ続きがあるかどうか」を教えてくれる仕組みです。
• console.log(iterator.next()); → ラウンド1
最初の 10 が出てきて「まだ続きあるよ!」
• console.log(iterator.next()); → ラウンド2
次は 20 が出てきて「まだ続きあるよ!」
• console.log(iterator.next()); → ラウンド3
次は 30 が出てきて「まだ続きあるよ!」
• console.log(iterator.next()); → ラウンド4
あれれ、もう値がないので「終わりだよ!」
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());


{ value: 10, done: false }
{ value: 20, done: false }
{ value: 30, done: false }
{ value: undefined, done: true }
実行結果
{ value: 10, done: false }
{ value: 20, done: false }
{ value: 30, done: false }
{ value: undefined, done: true }4. カスタムオブジェクトに Symbol.iterator を実装する





これは「自作オブジェクトを for…of で回せるようにする仕組み」のデモです。Symbol.iterator を実装すると、オブジェクトを配列のように順番に取り出せるようになります。
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { done: true };
}
};
}
};
for (const num of range) {
console.log(num);
}


まず、さっきのコードを分解して説明します。



range は start と end というプロパティを持つ普通のオブジェクトです。
start は 1 から始まることを、end は 5 までで終わることを表します。
このままでは for…of で回すことはできません。
const range = {
start: 1,
end: 5,
...
};


そこで Symbol.iterator という特別なキーを実装すると、「このオブジェクトは反復可能(iterable)」と認識され、for…of で扱えるようになります。
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { done: true };
}
};
}


Symbol.iterator が返すのは イテレーターオブジェクト です。
このオブジェクトには next() メソッドがあり、呼び出すたびに { value, done } を返します。
• value → 今の値
• done → まだ続きがあるかどうか
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { done: true };
}
};


for…of 構文は内部的に range[Symbol.iterator]() を呼び出し、done: true が返るまで next() を繰り返します。
for (const num of range) {
console.log(num);
}


1. range[Symbol.iterator]() が呼ばれる
→ current = 1, end = 5 のイテレーターが返る
2. next() 1回目 → { value: 1, done: false }
3. next() 2回目 → { value: 2, done: false }
4. …
5. next() 5回目 → { value: 5, done: false }
6. next() 6回目 → { done: true } → ループ終了



1, 2, 3, 4, 5
実行結果
1, 2, 3, 4, 5


要するに、Symbol.iterator がなければ for…of は使えません。ちょうど、歯車がないと時計が動かないようなものです。
5. 注意点・落とし穴


for…in と for…of の混同



for…in と for…of は見た目が似ていますが、返すものが異なります。
• for…in → オブジェクトの「キー(プロパティ名)」を列挙する構文。配列に使うとインデックスが返ります。
• for…of → iterable の「値」を列挙する構文。配列に使うと要素そのものが返ります。



配列 [“a”,”b”,”c”] に対して for…in を使うと、要素そのものではなく インデックス(0,1,2) が返ります。
const arr = ["a", "b", "c"];
for (const key in arr) {
console.log(key);
}


0, 1, 2
実行結果
0, 1, 2


for…of は値をそのまま返してくれます。
配列 [“a”,”b”,”c”] なら、順番に “a” → “b” → “c” が取り出されます。
const arr = ["a", "b", "c"];
for (const value of arr) {
console.log(value);
}


a, b, c
実行結果
a, b, c


つまり、for…inとfor…ofは似てるので間違いやすい。そこは気をつけてください。
オブジェクトリテラルはデフォルトで iterable ではない:必要なら自分で Symbol.iterator を実装する。
なぜオブジェクトは iterable じゃないのか
• 配列や文字列は「順番に値を取り出す」仕組みがあるため、for…of で回せる。
• 一方、オブジェクトリテラル { a: 1, b: 2 } は「順序付きの値の列」というより「キーと値のマップ」なので、デフォルトでは iterable ではない。



わかりやすくいうと、オブジェクトリテラル {} はiterableに対応していないからです。
そのため以下のように書くとエラーになります。
const obj = { a: 1, b: 2 };
for (const v of obj) {
console.log(v);
}


…?
for…of 構文だと?しかも、オブジェクトリテラル {} だって?そんなの話は聞いてないよ…
TypeError: obj is not iterable
(そのオブジェクトは iterable(反復可能) ではないので、for…of やスプレッド構文などで使えません)
実行結果
TypeError: obj is not iterable
自分で Symbol.iterator を実装する



{} はそのままでは iterable じゃないので、対応させたいなら Symbol.iterator を実装してあげる必要があります。
const obj = {
a: 1,
b: 2,
*[Symbol.iterator]() {
for (const key of Object.keys(this)) {
yield [key, this[key]];
}
}
};
for (const [k, v] of obj) {
console.log(k, v);
}


…
あ、Symbol.iteratorがあるから反復だったのね
a 1
b 2
実行結果
a 1
b 26. まとめ


JavaScript の反復処理を支えているのは、目に見えない小さな歯車 Symbol.iterator です。
配列や文字列が当たり前のように for…of で回せるのも、裏でこの歯車が動いているから。
一方で {} はその歯車を持っていない。だから「回そうとすると止まってしまう」。
でも、自分で Symbol.iterator を実装すれば、オブジェクトだって立派にレーンに乗って流れ始める。
要するに、
• なぜ配列は回せるのか
• なぜオブジェクトは回せないのか
• どうすれば回せるようになるのか
これを理解すれば、JavaScript の「反復処理」という舞台裏が一気にクリアになります。
「ただの構文」だと思っていた for…of の奥に、こんな仕組みが隠れていたんだ――
そう気づいた瞬間、コードの見え方が変わります。
もしこの記事が役に立ったと思ったら、シェアやコメントで教えてください。
いただいた声を今後の改善に活かしていきます。
最後まで読んでくださり、本当にありがとうございました。









コメント