あれれ。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