イテレータ
JavaScriptを使っているとコレクションの操作が不便で仕方がない。ES5でforEach()
とかmap()
とかが出てきてだいぶマシにはなったけれど、Array
以外のオブジェクトは色々ハックしなくちゃいけないから、まだまだ面倒くさい。
いい加減にイテレータを使いたい、からES6のイテレータについて調べた。
仕様
ECMAScript6のドラフト(rev24 2014年4月27日)を読んだ。Iterable
とIterator
という見た目が似ている2つのインターフェイスがあって少し混乱しやすい。
Iterable
とIterator
Iterable
は、呼び出すとIterator
を返す[Symbol.iterator]()
というメソッドを持たなきゃいけない。Symbol
についてはこことかここで。Array
とかMap
とかはこのIterable
を実装している。
var array = [1, 2, 3];
var iterator = array[Symbol.iterator]();
Iterator
はnext()
というメソッドを持つ。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
を作る。そのIterator
のnext()
メソッドをループごとに呼び出す。
for (var value of array)
console.log(value);
// 1 2 3
ややこしいことにArray
の返すIterator
はIterable
でもある。でもそのおかげで、for-of に渡せる。
for (var val of array[Symbol.iterator]())
console.log(val);
// 1 2 3
generator
ジェネレータというのもあってIterator
を返す関数を書きやすくしたもの。ジェネレータが返すIterator
もIterable
を実装している。function*
とyield
とyield*
という構文で定義できる。
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()
メソッドの話とかもスルーしている。
参考
- The Iterator protocol - JavaScript | MDN
- 古いFirefoxの実装とか
- Iterator current/prev value
current
とかprev
とかのプロパティがない理由
- Generator * syntax
yield
がES5だと strict mode 以外では予約語じゃないって話
- Final iterator spec
- そのオブジェクトが
Iterable
かどうやって調べるのか、とか
- そのオブジェクトが