IteratorとGenerator

社内LT用です。

* * *

# Iteratorをおさらい

```js
ary = [0, 1, 2];
it = ary[Symbol.iterator]()
console.log(it.next());
// { done: false, value: 0 }
console.log(it.next());
// { done: false, value: 1 }
console.log(it.next());
// { done: false, value: 2 }
console.log(it.next());
// { done: true, value: undefined }
```

* * *

# for of構文によるループ

```js
ary = [0, 1, 2];
it = ary[Symbol.iterator]();
for (value of it) {
  console.log(value);
  // 0, 1, 2
}
```

* * *

> イテレーターとは、一連の処理中において現在の処理位置を把握しつつ、コレクション中の項目へ一つずつアクセスする方法を備えたオブジェクトのことです。

MDN [イテレーターとジェネレーター](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Iterators_and_Generators) より

* * *

> JavaScript においては、イテレーターは一連の処理中の次の項目を返す next() メソッドを提供するオブジェクトです。このメソッドは done と value という 2 つのプロパティを持つオブジェクトを返します。

MDN [イテレーターとジェネレーター](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Iterators_and_Generators) より

* * *

# next() を 実装してみる

```js
const generateIterator = function() {
  let count = 0;
  return {
    next: function() {
      if (count > 2) return { done: true, value: void(0) };

      return { done: false, value: count++ }
    }
  }
}

const it = generateIterator();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
```
* * *

# `{ done: boolean, value: any }` はIteratorResultオブジェクト

* * *

# sampleIteratorは `for of` によるループでは利用できない

* * *

> iterableになるために、オブジェクトは@@iteratorメソッドを実装する必要があります。これは、オブジェクト(または、prototype chainオブジェクトの一つ)がSymbol.iteratorキーのプロパティを持つ必要があります。:

MDN [反復処理プロトコル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Iteration_protocols) より

* * *

# どうする

* IteratorをさらにIterableにする必要がある
  * `Iterator[Symbol.iterator]()` を持つ必要がある
    * Iteratorそのものが返り値となる関数
  * ArrayやMapはもともとIterable

* * *

# `[Symbol.iterator]()` を実装する

```js
const generateIterator = function() {
  let count = 0;
  return {
    [Symbol.iterator]: function() {
      return this;
    },
    
    next: function() {
      if (count > 2) return { done: true, value: void(0) };

      return { done: false, value: count++ }      
    }
  }
};

const it = generateIterator();

// for of
for (value of it) {
  console.log(value);
  // 0, 1, 2
}
```

# だるい

* そこでGeneratorですよ

* * *

# Generator

```js
function* it() {
  let count = 0;
  while(true) {
    if (count > 2) break;
    yield count++;
  }
}

for (value of it()) {
  console.log(value);
  // 0, 1, 2
}
```

超スッキリ

* * *

# ここが違う

* functionキーワードの後に `*`
* `return` の代わりに `yield`

* * *

# yield

* IteratorResultを返す
* yieldに続く式を評価した結果が `value` に代入される
* yield式によって関数は実行を停止する
  * `next()` が呼ばれると実行が再開する
  * 再開後 `return` もしくは何もせずに関数が終了した場合 `done:true` でIteratorResultが返る

* * *

# Iteratorの利点

* オブジェクトや関数をIterableにすることで　`for of` に突っ込める
* スプレッド演算子による式展開が行える
* 分割代入が行える
* range関数が簡単にかける

* * *

# スプレッド演算子による式展開が行える

```js
function* it() {
  yield "hoge";
  yield "hige";
  yield "fuga";
}

console.log(...it());
// hoge hige fuga

```

* * *

# 分割代入が行える

```js
function* it() {
  yield* [0, 1, 2];
}

[a, b, c] = it();

console.log(a); // 0
console.log(b); // 1
console.log(c); // 2
```

* * *

# range関数が簡単にかける

```js
function* range(start, end) {
  let n = start;
  while(true) {
    yield n++;
    if (n > end) break;
  }
}

const r = [...range(3, 6)];
console.log(r); // [3, 4, 5, 6]
```

* * *

# 参考にさせていただいたページ

* [JavaScript の ジェネレータ を極める！](http://qiita.com/kura07/items/d1a57ea64ef5c3de8528)
* [JavaScript の イテレータ を極める！](http://qiita.com/kura07/items/cf168a7ea20e8c2554c6)
* [イテレーターとジェネレーター](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Iterators_and_Generators)
* [反復処理プロトコル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Iteration_protocols)
* [yield](https://developer.mozilla.org/ja/docs/JavaScript/Reference/Operators/yield)