ひよこ3:07のTechブログ

イテレータの実装

実装

仕様についてこの前調べた。

実装がどうなっているかも確認する。

Chrome

Chrome はそのままだとどのバーションでも、イテレータ関係の機能は全く使えない。chrome://flags/#enable-javascript-harmonyを有効にする必要がある。

Chrome 35.0.1916.153 stable だと V83.25.28.18array.keysarray.valuesarray.entriesが存在する。for-ofはIterableではなくIteratorを渡す場合動く。つまり、for (var a of [0, 1, 2]) ...はダメだけどfor (var a of [0, 1, 2].values())なら期待通り動く。ジェネレータも動いた。

Chrome 38.0.2086.3 canary では V83.28.19 になってWeakMap、WeakSet以外はfor-ofもジェネレータもひと通り使えるようになっていた。かなりいいかんじ。

Node

Node でも--harmonyフラグを付けないとイテレータ関係の機能は使えない。

Node 0.10.29V83.14.5.9 で、一応MapSetWeakMapのコンストラクタが存在する。ただし、イテレータ周りの実装はなくて全く使えない。

Node 0.11.13 では V83.25.30 になって、SymbolArray.keysなどが実装されfor-ofはIterableじゃなくてIteratorを受け取る、Chrome 35 と同じような動作をする。ジェネレータも動いた。

Firefox

for-ofとArray@@iteratorは13の時から使えるらしい[参考]MapSet@@iteratorは17から使える。ジェネレータも使えた。

Firefox 30 で確認したら、ArrayMapSetはなぜかarray.valuesはないのを除けばメソッドも含めて実装されていて、WeakMapのコンストラクタも存在していた。

ただし、Symbolが実装されたのは33かららしい[参考]Iterableなオブジェクトには、Symbol.iteratorではなく'@@iterator'という文字列をキーに使っている。

Firefox 33.0a1 nightly で確認したらSymbolはあったけど、@@iteratorはまだ'@@iterator'という文字列をキーに使っていた。おそらくこれから移行していくんだと思う。このバージョンでもarray.valuesはなぜかない。

Safari/WebKit

Safari 7.0.5 はイテレータ関係は全くダメ。

Webkit nightly build 7.0.5 9537.77.4, r170987で確認したら、array.values[Symbol.iterator]を除いて、ArrayMapSetがメソッドも含めて実装されていた。ただし、array.keysとかが返すオブジェクトが謎。nextとかのメソッドは存在しない。for-ofにはarrayarray.keysなどが返すオブジェクトを渡すことができたけど、自分でIterableとかIteratorを作って渡すのは無理だった。

なんか1番変な実装だった。Yosemiteで良くなるのかな?わからない。

IE

知らない

Transpilers

  • regenerator
    • ジェネレータをES5に変換する
    • ジェネレータはSymbol.iteratorがあればそれを、なければ'@@iterator'を使うっぽい。[参考]
  • es6-transpiler
    • ES6の構文をES5に変換する
      • ジェネレータは未対応
    • spread、for-of、array comprehensionsでIterableなオブジェクトを使えるっぽい
    • Symbol.iteratorがあればそれを、なければ'@@iterator'を使うっぽい[参考]
  • google/traceur-compiler
    • ES nextをES5に変換する
    • SymbolMapSetも面倒見てくれる
      • WeakMapとかはない
      • 実行環境が中途半端に実装していると、それ以上は面倒見てくれない
    • Symbol.iteratorを使う

Polyfills

  • es6-mapes6-set
    • それぞれMapSetのpolyfill
    • Symbol.iteratorがあればそれを、なければ'@@iterator'を使う
  • es6-shim
    • いろんなオブジェクトのpolyfill
    • Symbol.iteratorがあればそれを、なければ'_es6shim_iterator_'を使う
      • ただし、Firefoxの場合は'@@iterator'を使う
      • 少し複雑

まとめ

  • 現状イテレータをそのまま使える処理系はなさそう
    • そもそも実装されていなかったり、フラグが必要だったり
  • for-ofとかジェネレータを使う場合transpilerが必要
  • 中途半端に実装されている環境を考慮するといろいろつらい
  • 自分の作ったライブラリをIterator対応させたい場合iteratorメソッドを作って、それをSymbol.iterator'@@iterator'に入れておくのがいいかもしれない
var AwesomeObject = function() {};

AwesomeObject.prototype.iterator = function(){
    // iteratorを返す素晴らしい処理
};

if (typeof Symbol !== 'undefined' && Symbol.iterator)
  AwesomeObject.prototype[Symbol.iterator] = AwesomeObject.prototype.iterator;
AwsomeObject.prototype['@@iterator'] = AwesomeObject.prototype.iterator;
AwsomeObject.prototype['_es6shim_iterator_'] = AwesomeObject.prototype.iterator;