ひよこ3:07のTechブログ

ES6のイテレータについて調べた

イテレータ

JavaScriptを使っているとコレクションの操作が不便で仕方がない。ES5でforEach()とかmap()とかが出てきてだいぶマシにはなったけれど、Array以外のオブジェクトは色々ハックしなくちゃいけないから、まだまだ面倒くさい。

いい加減にイテレータを使いたい、からES6のイテレータについて調べた。

仕様

ECMAScript6のドラフト(rev24 2014年4月27日)を読んだ。IterableIteratorという見た目が似ている2つのインターフェイスがあって少し混乱しやすい。

IterableIterator

Iterableは、呼び出すとIteratorを返す[Symbol.iterator]()というメソッドを持たなきゃいけない。Symbolについてはこことかここで。ArrayとかMapとかはこのIterableを実装している。

var array = [1, 2, 3];
var iterator = array[Symbol.iterator]();

Iteratornext()というメソッドを持つ。next()はイテレーションが終わりかどうかを示すdoneと値valueを持つオブジェクトを返す。

iterator.next(); // => { done: false, value: 1 }
iterator.next(); // => { done: false, value: 2 }
iterator.next(); // => { done: false, value: 3 }
iterator.next(); // => { done: true, value: undefined }

for-of

for-in 構文はオブジェクトのキーをイテレートする。使いづらい。

ES6で新しく追加された for-of 構文はIterableを受け取ってIteratorを作る。そのIteratornext()メソッドをループごとに呼び出す。

for (var value of array)
    console.log(value);
// 1 2 3

ややこしいことにArrayの返すIteratorIterableでもある。でもそのおかげで、for-of に渡せる。

for (var val of array[Symbol.iterator]())
    console.log(val);
// 1 2 3

generator

ジェネレータというのもあってIteratorを返す関数を書きやすくしたもの。ジェネレータが返すIteratorIterableを実装している。function*yieldyield*という構文で定義できる。

function* gen() {
    for (;;) {
        yield 1;
        yield* [2, 3];
    }
}

for (var value of gen())
    console.log(value);
// 1 2 3 1 2 3 1 2 3 1 2 3 ...

yield*に渡すオブジェクトもIterable

arrow-functionとは組み合わせて使えない。あんまりだ。[参考]

comprehension

IterableからArrayとかを作る構文がある。

[for (x of [1, 2, 3]) x * x]; // => [1, 4, 9]

var iterable = (for (x of [1, 2, 3]) if (x % 2 === 1) x);

for (var value of iterable)
    console.log(value);
// 1 3

spread

Iterable...をつけて引数に渡すことができる。

function f(a, b, c) {
    return [a, b, c];
}

f(1, ...[2, 3, 4]); // => [1, 2, 3]

destructuring

ES6では複数の変数にIterableをまとめて代入ができる。

var [a, b, c] = [1, 2, 3];
a; // => 1
b; // => 2
c; // => 3

関数の引数も。

function add([a, b]) {
    return a + b;
}

add((function*() {
    yield 1;
    yield 1;
}())); // => 2

Iterableを受け取るメソッド・コンストラクタ

ES6で増えたメソッド・コンストラクタにはIterableを引数として受け取るものがある。

  • Array.from(iterable)
  • Promise.all(iterable)
  • Promise.race(iterable)
  • new Map(iterable)
  • new Set(iterable)
  • new WeakMap(iterable)
  • new WeakSet(iterable)

実装とかPolyfillとか

実装とかPolyfillとかがどうなっているのかもまとめようと思ったけど、なんか思ったより長くなったから次の投稿で。

throw()メソッドの話とかもスルーしている。

参考