Nicholas C. Zakas is a web software engineer who specializes in user interface design and implementation for web applications using JavaScript, Dynamic HTML, CSS, XML, and XSLT.

JavaScriptのNoteListオブジェクトは特殊なオブジェクトだ。同じオブジェクトであるにもかかわらず、動的なオブジェクトと静的なオブジェクトという2種類の実装を持っている。この違いが性能に大きな差を与えている。このあたりの話がWhy is getElementsByTagName() faster that querySelectorAll()? - NCZOnlineにわかりやすくまとまっており参考になる。

JavaScriptには特定の要素のリストを取得する次の方法がある。

  • getElementsByTagName();
  • querySelectorAll();

Nicholas C. Zakas氏はTwitterのつぶやきを見ていて、この2つの処理時間が大きく違うことに気がついたという。Windows XPのFirefox 3.6.8でgetElementsByTagName("a")とquerySelectorAll("a")を実行して比べてみたところ、querySelectorAll("a")の方が98%も遅かったという。この性能の違いはNoteListオブジェクトに2つの種類があることが理由だと説明されている。NoteListオブジェクトには次の2つの実装がある。

  • 動的なNodeList - ページを表現するDOMにリアルタイムに接続したオブジェクト。このオブジェクトを経由して行われた変更はほかのすべての動的なNodeListにも反映されることになる。つまり、単一のDOMへのリンクになっている。
  • 静的なNodeList - 動的ではないNodeList。生成した段階のDOMがコピーされるもので、このNodeList経由でデータを変更しても、ほかのNodeListには影響を与えない。まったく独立している。

結果として返ってくるNodeListの種類は次のように違っている。

  • getElementsByTagName() - 動的なNodeList
  • querySelectorAll() - 静的なNodeList

動的なNodeListオブジェクトの生成は要するにデータに対するエントリを追加する実装になる。シャローコピーといえる。これに対して静的なNodeListオブジェクトの生成は実際に関連するデータをすべてコピーして独立したものして保持する実装になる。ディープコピーというわけだ。実装的にみてディープコピーはシャローコピーよりもコストのかかる処理となる。実装を工夫することで改善は可能だろうが、本質的に静的なNodeListを生成するquerySelectorAll()はgetElementsByTagName()よりも処理時間がかかることになる。

Nicholas C. Zakas氏はこうした特性を踏まえたうえで、次のように使いわけるのがいいだろうと指摘している。

  • 要素のリストがほしいだけなら高速に動作するgetElementsByTagName()を使う。
  • 複雑なCSSクエリを発行する必要がある場合にはquerySelectorAll()を使う。
  • その時点での要素のスナップショットがほしいならquerySelectorAll()を使う。