iterator and generator for it in JavaScript

つぎ。 iterator ( https://developer.mozilla.org/ja/New_in_JavaScript_1.7#.e3.82.b8.e3.82.a7.e3.83.8d.e3.83.ac.e3.83.bc.e3.82.bf.e3.81.a8.e3.82.a4.e3.83.86.e3.83.ac.e3.83.bc.e3.82.bf ) 。 for in や for each in 内では独自に定義した iterator に従って走査されるというのはありがたいかも。解説は MDC や http://nanto.asablo.jp/blog/2006/08/12/481381#js17-iteratorhttp://nanto.asablo.jp/blog/2006/08/12/481381#js17-generator を見てもらったほうが早いんだけど、なんか ECMAScript 4 の draft ( http://www.ecmascript.org/es4/spec/overview.pdf ) を見ていると全然互換性のない表記が出てくるから将来的にこういう形で recommend されるのかわかんない。どうも sample code っぽいものを見てると class とか IterableType とか public とか普通に使われてるので ActionScript よりになるのかな ?

で、実際のコードだと Vimperator での使われ方に感動した。具体的には buffer.evaluateXPath() 関数の一番下。

evaluateXPath: function (expression, doc, elem, asIterator)
{
    if (!doc)
        doc = window.content.document;
    if (!elem)
        elem = doc;

    var result = doc.evaluate(expression, elem,
        function lookupNamespaceURI(prefix)
        {
          switch (prefix)
          {
            case "xhtml":
              return "http://www.w3.org/1999/xhtml";
            default:
              return null;
          }
        },
        asIterator ? XPathResult.UNORDERED_NODE_ITERATOR_TYPE : XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
        null
    );

    result.__iterator__ = asIterator
                        ? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
                        : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); };

    return result;
},

こうすると型の互換性に注意しなくてもよくなる。例えば自作の evaluate wrapper 関数だと今までひっかっかった snapshotItem を配列に詰め直して返してたんだけど

function $s(query, node, isChrome) {
    node = node || isChrome ? window.content.document : document;
    var result = (node.ownerDocument || node).evaluate(
        query,
        node,
        null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
        null
    );
    var nodes = [];
    for(var i=0 ; i<result.snapshotLength ; ++i) nodes.push(result.snapshotItem(i));
    return nodes;
}

こんなことしなくても yield しちゃえばいいよってことだね。あとこれは正直原因がわかんないんだけど、 vimp が定義してる utility 関数に util.Array.iterator てのがあって配列の値に対して配列内包表記を使いたい場合に使ってたりするんだけど

// 属性とその値を '=' でつないだものが出てくる
// ex: ['id="foo"', 'class="bar"']
[a.name + '="' + a.value + '"' for (a in util.Array.iterator(elem.attributes))]

冗長だなぁと思って for each を使うようにすると undefined が余計につく。

// ex: [undefined="undefined", 'id="foo"', 'class="bar"']
[a.name + '="' + a.value + '"' for each (a in elem.attributes)]

なんでだろう。 util.Array.iterator で特に何かしてるわけじゃないみたいだし。

const util = {
    Array: {
        iterator: function (ary) {
            let length = ary.length;
            for (let i = 0; i < length; i++) {
                yield ary[i];
            }
        }
    },
};