Jintrick.netagenda2007年05月アーカイブ → 2007年05月30日

Javascript1.7のGenerator

あれれ。Generatorが使えるようになってるじゃあないか。それならPythonみたいにNodeListを扱えるかと思って、次を試してみた。

NodeList.prototype.items = function(){
    for (let i=0, len=this.length; i<len; i++) {
         yield this.item(i);
    }
};
var nl = document.getElementsByTagName("P");
for each(let elm in nl.items()) {
    alert(elm.textContent);
}

これならfor eachでNodeListを扱うときに余計な判別式を使わなくてすむ。と思いきや何故かnl.itemsがundefined。nl instanceof NodeListはtrueなのにね。まあ実装方法はこうでなくてもいいわけで、とりあえずGeneratorの動作を確認すべく再試行した。

var nl = document.getElementsByTagName("P");
for each(let elm in NodeList.prototype.items.apply(nl)) {
    alert(elm.textContent);
}

ちなみにJavascript1.7のにゅーフィーチャー「destructuring assignment」を使うとさらにPythonicなスクリプトが書けるよ。

var nl = document.getElementsByTagName("P");
for each(let {textContent: c, attributes: a} in NodeList.prototype.items.apply(nl)) {
    let s = "";
    for each(let {name: n, value: v} in NodeList.prototype.items.apply(a)) {
        s += n + " = " + v + "\n";
    }
    s += c;
    alert(s);
}

prototypeチェインが繋がらないのが謎だけど、チェインに拘らなければ別に気にならない。そもそも上の例ではprototypeチェインでitemsジェネレータを繋ごうとすると、継承関係のないNamedNodeMapとNodeListそれぞれに同じことをしなければならない(まあできないんだけど)。だったらクラスメソッドにでもしておいた方が良いのかも。

NodeListは「生きて」いるのでlengthプロパティはgetterを呼ぶ。これではforループで毎回呼び出すことになるだろう。しかもgetElementsByTagName("*")でその回数は膨大。今まではlengthプロパティの「値」を参照する代案を提示してきたが、Greasemonkey scriptということでJavascript1.6が使える前提で例を。

var allElements, thisElement;
allElements = document.getElementsByTagName('*');
for each (thisElement in allElements) {
	if (!(thisElement instanceof Element)) continue;
	// do something with thisElement;
}

参照:Core JavaScript 1.5 Reference - MDC


webmaster@jintrick.net
公開: 2007年05月30日
カテゴリ: DOM ,Javascript