JavaScript | ES6+ 文法:その他の ES6+ 機能 – Iterator

JavaScript JavaScript
スポンサーリンク

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,
    };
  },
};
JavaScript

next() を呼ぶたびに、この { 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 }
JavaScript

for…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 }
JavaScript

Map / 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 } など
JavaScript

Map の場合、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)」が動いているイメージが、きっと掴めてくるはずです。

タイトルとURLをコピーしました