素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: twitter: @javascripter にどうぞ。

XPathはDOM+JSより速いのか

XPathと、getElementsBy〜の速度比較。

はてなハイク - お絵かきも楽しめるミニブログからclass名がentryである要素を抽出する場合。

console.time('speed');
for(var i=0;i<100;i++)document.getElementsByClassName('entry')
console.timeEnd('speed');

console.time('speed');
for(var i=0;i<100;i++)document.evaluate('//*[@class="entry"]',document,null,7,null)
console.timeEnd('speed');

DOM:speed: 2ms
XPath:speed: 123ms
単純にクラス名で指定する場合は圧倒的にgetElements〜のほうが速い。しかも生きたNodeList。

では、もう少し複雑な場合。
Bookmarklet - はてなハイクから、class名がprofile-imageで、altがjavascripterであるimg要素を抽出する場合。

console.time('speed');
for(var i=0;i<100;i++)Array.filter(document.getElementsByClassName('profile-image'),function(i)i.alt=='javascripter');
console.timeEnd('speed');

console.time('speed');
for(var i=0;i<100;i++)document.evaluate('//*[@class="profile-image" and @alt="javascripter"]',document,null,7,null)
console.timeEnd('speed');

DOM:speed: 36ms
XPath:speed: 109ms
まだDOMのほうが速い。

もっと複雑な場合。
はてなハイク - お絵かきも楽しめるミニブログから、親要素がpで、そのクラス名がusernameで、子のa要素のテキストがjavascripterである要素を取得する場合。

console.time('speed');
for(var i=0;i<100;i++)document.evaluate('//a[../../p[@class="username"] and text()="javascripter"]',document,null,7,null);
console.timeEnd('speed');

console.time('speed');
for(var i=0;i<100;i++)Array.filter(document.getElementsByTagName('a'),function(i)i.parentNode.className=='username' && i.textContent=='javascripter');
console.timeEnd('speed');

DOM:speed: 176ms
XPath:speed: 78ms

XPathのほうが速い。

ということで、取得するものが単純な場合はDOMを使って、
Greasemonkeyとかで他人のサイトを弄る場合のように、自由にできないときはXPathを使えばいいと思う。

100回して2秒未満だからそこまで気にする必要ないとは思うけど。

try〜catch文の正しい使い方

try〜catch文は、エラーを処理するときに使う。

たとえば、ユーザーにXPathを入力してもらい、document.evaluateでそれを評価する時、ユーザーが間違ったXPathを入力するかもしれない。
上の内容をそのままコードにすると、

var xpath=prompt('type XPath');
try{
document.evaluate(xpath,document,null,7,null);
}
catch(e){

if(e.message=='The expression is not a legal expression.')alert('XPath式が間違っています')
}

のようになる。

一見これでよさそうだが、これにはひとつ間違いがある。
try文の中で意図しないエラーが発生した場合にもcatch文で吸収されてしまうので、debugが困難になってしまうのだ。

正しくは

var xpath=prompt('type XPath');
try{
document.evaluate(xpath,document,null,7,null);
}
catch(e){

if(e.message=='The expression is not a legal expression.'){
alert('XPath式が間違っています')
}else{
throw e;
}

}

とするべきである。
こうすると、

var xpath=prompt('type XPath');
try{
document.evaluate(xpath,document,null,7,null));//括弧が多いのでエラーになる
}
catch(e){
if(e.message=='The expression is not a legal expression.'){
alert('XPath式が間違っています')
}else{
throw e;
}
}

のように、コード自体が間違っている場合にちゃんとエラーが投げられる。

というようなことを思いついたけど、実際にそうやってるコードを見たことが無いし、本当にそんなことをするべきなのかどうかは分からない。

追記:サイ本の98ページあたりにthrowで投げなおすとかそういうことが書いてあった。上のコードは珍しいものじゃなくてごく普通のコードのようだ。