32743


**「ジェネレーター」と「Symbol.iterator」は何が違うのでしょうか？**  
・何れも「iteratorオブジェクト」を返すと思うのですが…


CC BY-SA 3.0
2017-02-17T01:40:47.500
32731
2017-02-24T17:52:37.937
2017-02-24T17:52:37.937
3054
7886
question
javascript ecmascript-6
「ジェネレーター」と「Symbol.iterator」は何が違うのでしょうか？


様々なオブジェクトが Iterator を通した反復をサポートすることを考えたとき、どのオブジェクトに対しても同じ方法で Iterator
の取得が行えると便利です。そこで ES6 では Symbol を使い、`foo[Symbol.iterator]()` として Iterator
を取得するように決めました。このようにして Iterator を取得できるオブジェクトを Iterable オブジェクトと呼びます。

Iterator は [Iterator
プロトコル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Iteration_protocols#iterator)に沿って
`next()` を実装しているオブジェクトで、

  * 配列などで使われる組み込みの Iterator
  * ジェネレーター関数で生成したジェネレーターオブジェクト
  * 自前で `next()` を実装したオブジェクト

などがあります。実装方法は異なりますが、どれも Iterator です。どれを `[Symbol.iterator]()` で返しても構いません。

※ちなみに組み込みのイテレータ、およびジェネレーターは [IteratorPrototype](http://www.ecma-
international.org/ecma-262/6.0/#sec-%25iteratorprototype%25-object)
と呼ばれる隠しオブジェクトを継承しますが、これは Iterator プロトコルには含まれておらず、自作 Iterator がそうする必要はありません。

* * *

（前の版でせっかく書いたので）ジェネレーターについてもう少し説明しておきます。

Iterator オブジェクトは「次の値かもう値がないことを返す」 `next()`
メソッドがあればいいので、自分で作ることもできます。ただ、一つ値を取り出すごとに return
しないといけないので、今どこまで値を取り出したかという内部状態を自分で管理しなければなりません。

例えば次のコードは 0..5..0 と順番に整数を表示します。

[code]

    for (let i = 0; i <= 5; ++i)
      console.log(i);
    for (let i = 4; i >= 0; --i)
      console.log(i);
    
[/code]

この 0..5..0 と整数を返す部分を Iterator として書くとどうなるでしょうか。

[code]

    let iterable = {};
    iterable[Symbol.iterator] = function() {
      return {
        _fwd: true,
        _cur: -1,
        next: function() {
          if (this._fwd) {
            ++this._cur;
            if (this._cur === 5) this._fwd = false;
            return { value: this._cur, done: false };
          } else {
            if (this._cur >= 1) {
              --this._cur;
              return { value: this._cur, done: false };
            } else {
              return { done: true };
            }
          }
        }
      };
    };
    
    for (let i of iterable)
      console.log(i);
    
[/code]

forが使えないうえ、途中で動作を切り替えるためにその状態も保持しなければなりません。

ここで Generator 構文が登場します。これは yield で値を返す際に関数の状態を保存しておき、次に `next()`
が呼ばれたときにはそこから再開してくれます。

[code]

    let iterable = {};
    iterable[Symbol.iterator] = function* () {
      for (let i = 0; i <= 5; ++i)
            yield i;
      for (let i = 4; i >= 0; --i)
            yield i;
    };
    
    for (let i of iterable)
      console.log(i);
    
[/code]

このように Iterator を簡単に作成できるのがジェネレーター構文です。

とはいえ、ジェネレーターを使うか使わないかは実装方法の違いでしかなく、どちらの方法で作っても Iterator であることには変わりありません。


CC BY-SA 3.0
2017-02-17T06:53:58.753
32743
2017-02-17T07:42:53.290
2017-02-17T07:42:53.290
8000
8000
32731
answer