JavaScript　イテレータとジェネレータ

JacaScriptのイテレータとジェネレータの概念について、自分自身が理解するためにもその基本をまとめます。

# JavaScriptのイテレーターとイテラブル
### ■ イテレーターとは
シーケンス(配列やオブジェクトなど)を反復処理するためのオブジェクトであり、各要素を順番に返すためのオブジェクトを指します。イテレーターは以下の特徴を持っています。

* **プロトコルの実装**
    イテレーターは```next()メソッド```を持ち、このメソッドは<strong>次の値(value)</strong>と、<strong>シーケンスが終了したかどうかを示すフラグ(done)</strong>を返します。返されるオブジェクトは ```{value, done}``` という形式です。
    
    ・value: 現在の要素の値
・done: シーケンスが終了した場合はtrue、まだ要素が残っている場合はfalse

```iterator.js
function iteratorSample(array){
    let index = 0;

    return {
        next: function(){
            if(index < array.length){
                return {value: array[index++], done: false};
            }else{
                return {value: undefined, done: true};
            }
        }
    }
}

const iterator = iteratorSample([1, 2, 3]);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
```

### ■ イテラブルとは
イテラブルとは、反復可能なオブジェクトを指し、JavaScriptでは、**\[Symbol.iterator]()メソッド**を持つオブジェクトを意味します。iterator.jsの```const iterator```をイテラブルにするには、```const iterator```を返す [Symbol.iterator] メソッドを作ると、イテラブルオブジェクトを作成できます。

```sample.js
function iteratorSample(array){
    let index = 0;

    return {
        next: function(){
            if(index < array.length){
                return {value: array[index++], done: false};
            }else{
                return {value: undefined, done: true};
            }
        }
    }
}


const iterator = iteratorSample([1, 2, 3]);
const iterable = {};
iterable[Symbol.iterator] = () => {
    return iterator;
}

// iterator is not iterableのエラーが発生
// for(const x of iterator){
//     console.log(x);
// }

for(const x of iterable){
    console.log(x);
}

```

#### ■ 文字列はイテラブルである
文字列がイテラブルであることを、下記のコードで確認できます。

```sample.js
const str = "イテラブル";

// 1. [Symbol.iterator]メソッドを持っているかチェック
console.log(typeof str[Symbol.iterator]); // "function"

// 2. イテレータの取得と値の取得
const iterator = str[Symbol.iterator]();

console.log(iterator.next()); // { value: "イ", done: false }
console.log(iterator.next()); // { value: "テ", done: false }
console.log(iterator.next()); // { value: "ラ", done: false }
console.log(iterator.next()); // { value: "ブ", done: false }
console.log(iterator.next()); // { value: "ル", done: false }
console.log(iterator.next()); // { value: undefined, done: true }

// 3.for...ofループによる反復処理
for(const x of str){
   console.log(x); // イテラブル
}
```

# JavaScriptのジェネレーターとジェネレーター関数
ジェネレーターの解説をする際、**ジェネレーター**と**ジェネレーター関数** という2つのキーワードがでてきます。少しややこしいので、初めにその関係性を説明します。

**ジェネレータ関数は、特別な構文(function\* 構文)で定義された関数であり、その結果生成されるのがジェネレーターです。**

#### ■ ジェネレーターとは
ジェネレーターとは、イテレーターかつイテラブルであるオブジェクトのことを指します。

* **イテレーターである:** ```next()メソッド```を持ち、値を生成することができます。
* **イテラブルである:** ```[Symbol.iterator]()メソッド```を持ち、自身を返すことができるため、イテラブルなオブジェクトとしても機能します。

#### ■ ジェネレーター関数とは
ジェネレーター関数は、**function\*構文**を使用して定義される関数です。この関数では、**yieldキーワード**を使用して、一時停止しながら複数の値を生成することができます。最初に呼び出されると、ジェネレーター関数はコードを実行せず、ジェネレーターと呼ばれるある種のイテレーターを返します。

```sample.js
//　ジェネレータ関数　「function*」という構文を使用
function* generatorSample(){
    yield 1;
    yield 2;
    yield 3;
}

//　呼び出されると実行されず、代わりにジェネレータオブジェクトが返されます。
const gen = generatorSample();
console.log(typeof gen);// object
console.log(Object.prototype.toString.call(gen)) // [object Generator]

// イテレーター
console.log(gen.next()); // { value: 1, done: false }

// イテラブル
for (const value of gen) {
    console.log(value); // 2, 3
}
```

ジェネレーター関数は内部状態を保持することができるため、前回の呼び出し時の変数の値を引き続き使用することができます。
```sample.js
function* counter() {
    let count = 0;
    while (true) {
        yield count++;
    }
}

const gen = counter();
while ((result = gen.next()).value < 100) {
    console.log(result.value); // 0から99まで出力
}
```

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Iterators_and_generators


https://techblog.gmo-ap.jp/2024/11/19/js-generator-yield/