Firefox 17 から Map と Set がイテラブルになった件について

さて、Firefox 17 がリリースされました。そのリリースノートに、以下の様なのがあります。

JavaScript の Maps と Sets がイテラブル (反復可能なオブジェクト) として利用できるようなりました。

これはどういうことなのかを少し解説。といっても、内部のECMAScript仕様に詳しいわけではないので、その辺りは、Constellationさんに譲りたいと思います(ぉ あくまでMozilla実装での使い方ということで。

var map = new Map([
  ["a", "A"],
  ["b", "B"]
]);
map.set("c", "C");

console.log(
  map.get("a"),
  map.get("b"),
  map.get("c")
);
// "A B C"

ここまでは今までの基本です。

イテラブルとは

では、イテラブル*1とはどういうことか。

失敗例

for (var key in map) {
  console.log(key);
}

これは失敗です。for-in構文で回せるわけではありません。

for-of構文を使います。

for (var v of map) {
  console.log("%s: %s", v[0], v[1]);
}
/*
a: A
b: B
c: C
*/
// 以下の様に分割代入を使うのが良い感じ
for (var [key, value] of map) {
  console.log("%s: %s", key, value);
}
/*
a: A
b: B
c: C
*/

という具合です。

for-of 構文をもう少し突っ込んでみる

さて、for-of構文というと、配列とかHTMLCollection,NodeListに使うものでした。
単なるObjectに使うと....

var obj = {
  a: "A",
  b: "B"
};
for (var value of obj) {
  console.log(value);
} // TypeError: obj is not iterable

TypeError: obj is not iterableを例外が発生してしまい、単純なObjectにはfor-ofは使えないことが分かります。

ところで、Object.getOwnPropertyNames(Map.prototype)でメソッド一覧を見てみると、iteratorという見慣れないメソッドを発見できます。*2

実はこのメソッドがあるとfor-ofで使えるようになるのです。ただし、ECMAScript.next のドラフト仕様を見てもiteratorの事は載っておらず、今後載るのかもしれませんが、Mozilla独自仕様の可能性があります。

// enumrable: false で iterator メソッドを定義
Object.defineProperty(Object.prototype, "iterator", {
  value: function() {
    for (var key of Object.keys(this)) {
      yield [key, this[key]];
    }
    // 実はこの場合は return Iterator(this); でも可
  },
  configurable: true,
  writable: true,
  enumerable: false
});
var obj = {
  a: "A",
  b: "B",
  c: "C"
};
for (var [key, value] of obj) {
  console.log("obj %s: %s", key, value);
}
/*
obj a: A
obj b: B
obj c: C
*/

iteratorメソッドにはyieldを使わないとうまいこと動作しないことに注意してください。

応用すれば、以下の様な事も可能でしょう。あまり副作用を考えてませんが(ぉ

Object.defineProperty(HTMLElement.prototype, "iteratror", {
  value: function() {
    var attrs = this.attributes;
    for (var i = 0, len = attrs.length; ++i) {
      yield [attrs[i],name, attrs[i].value];
    }
  },
  configurable: true,
  writable: true
});

var elem = document.getElementById("foo");

for (var [attrName, attrValue] of elem) {
  // ...
}

*1:個人的には「イテレータブル」なんだけど、どうなんでしょう?
iterable,イテラブルが正解の様でした

*2:for-of で回せるもの、Array.prototype, HTMLCollection.prototype, NodeList.prototype にもあります