1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ひとりJavaScriptAdvent Calendar 2024

Day 9

【JavaScript】イテレーターって何?

Last updated at Posted at 2024-12-08

イテレーターとは

イテレーターは簡単に言うと、何かを順番に返すオブジェクトです。
これは特別な構文やコンストラクタで作るものではなく、特定の条件を満たすオブジェクトをイテレーターと呼びます。

イテレーターはnextメソッドを持ちます。
このメソッドは呼ばれるたびに以下のプロパティを持つオブジェクトを返す必要があります。

  • value: 返したい値(何を入れてもOK)
  • done: 返される値はこれで全てかどうかを示すboolean

例: 0~9を返す

例えば、0~9までの値を順番に列挙するイテレーターを考えてみます。

まず、イテレーターはオブジェクトなため、新しくオブジェクトを定義します。

const numberIterator = {}

次に、イテレーターにはnextメソッドが必要です。
nextメソッドはdonevalueプロパティを持つオブジェクトを返す必要があります。

const numberIterator = {
+ next() {
+   return { done: false, value: 0 } // 仮
+ }
}

そして、nextメソッドは呼び出しごとにvalueがインクリメントされている必要があります。
これにはインクリメント用の値を一時的に保管する場所が必要ですが、今回は特に工夫せずcurrentというプロパティに置いておきます。

const numberIterator = {
+ current: 0,
  next() {
+   return { done: false, value: this.current++ }
  }
}

最後に、this.current10以上になった後は{ done: true }を返すようにします。

全体像はこちらになります。

const numberIterator = {
  current: 0,
  next() {
    if(this.current >= 10) {
      return { done: true } // valueは省略可能
    }
    return { done: false, value: this.current++ }
  }
}

なお、このnextメソッドは呼び出すと以下のように値が返ってきます。

numberIterator.next()
> { done: false, value: 0 }
numberIterator.next()
> { done: false, value: 1 }
numberIterator.next()
> { done: false, value: 2 }
// 略
numberIterator.next()
> { done: false, value: 9 }
numberIterator.next()
> { done: true }

配列でよくない?

何か連続したものを表現したいなら、配列で事足りると思うかもしれません。
実際大半のイテレーターは配列で表現できますが、表現できないものもあります。

それは、無限に長さがあるものの表現です。

すべてのイテレーターを配列として表現できるとは想像するのは容易ですが、これは真実ではありません。配列は完全に割り当てなければなりませんが、イテレーターは必要なだけで消費されるため、0 から Infinity までの整数の範囲など、無限のサイズのシーケンスを表現できます。- MDN

先ほどの引用にある、0からInfinityまでの整数をイテレーターで表現してみます。
といっても特別な処理は必要なく、むしろ先ほどのコードから上限を削除するだけです。

const infinityIterator = {
  current: 0,
  next() {
    return { done: false, value: this.current++ }
  }
}

たったこれだけで、無限の長さのイテレーターを作れます。

また、イテレーターや後述のイテラブルは遅延評価の実装にも使えます。
こちらの記事で紹介されているメソッドを使ったり、専用の処理を自作するなどでできます。

イテラブル

イテラブルは[Symbol.iterator]メソッドを持つオブジェクトです。
これは反復可能オブジェクトとも呼ばれます。

[Symbol.iterator]メソッドはイテレーターを返す必要があります。

Symbol.iteratorウェルノウンシンボルのひとつです。

ウェルノウンシンボルはSymbolオブジェクトの静的プロパティに入っているSymbolオブジェクトのことで、使うとJavaScriptの組み込み動作をカスタマイズすることができます。

例えば今回のSymbol.iteratorは、オブジェクトをイテラブルというプロトコルに従わせることで、後述の「イテラブルが使える構文」が使えるようになります。

なお、組み込みのイテラブルなオブジェクトもあります。
例えばこのようなものがあります。

  • Array
  • Map
  • Set

[Symbol.iterator]の実装

例として1~10を示すイテラブルなオブジェクトを作ってみます。

こういったときにはクロージャのような実装ができます。

const iterable = {
  [Symbol.iterator]() {
    let current = 0 // 数値を一時保管する変数
    return { // イテレーターを返す
      next() {
        if(current >= 10) { // 10以上かチェック
          return { done: true }
        }
        return { done: false, value: current++ }
      }
    }
  }
}

実装手順はこのような感じです。

  1. [Symbol.iterator]メソッドを実装する
  2. メソッド内で数字を一時的に保管する変数(current)を宣言する
  3. [Symbol.iterator]がイテレーター(nextメソッドを持つオブジェクト)を返すようにする
  4. イテレーターのnextメソッドでcurrentを更新する
  5. current10以上かをチェックし、適切なオブジェクトを返す

イテラブルが使える構文

オブジェクトをイテラブルにするメリットの一つとして、いくつかの構文が使えるようになることが挙げられます。

イテラブルなオブジェクトは以下の構文が使えます。

for...of

各値を使ってループしたいときに使えます。
構文はfor (const 変数名 of イテラブル) { 処理 }です。

例えば、先ほどのiterableを使ってループしてみます。

for (const value of iterable) {
  // 処理
  console.log(value)
}

// 1
// 2
// 略
// 9
// 19

for...ofは配列の要素を使ってループしたいときにも使えますが、これは配列がイテラブルだからです。

スプレッド構文

...を使った構文です。
イテラブルなオブジェクトを配列(Array)に変換する時に使えます。

const array = [...iterable]
array // [1, 2, 3, ..., 9, 10]

また、関数の呼び出し時に引数を展開することもできます。
詳細はMDNをご覧ください。

その他

ここで紹介した以外にも、以下のような構文や言語仕様があります。

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?