jQueryネタで何か書こうとして下調べしていたら、意外なことに気づいたのでそれを記事化することにしました。
ノードのキャッシュ
JavaScriptで大量の要素を組み立てる場合、都度HTMLをパースしたり、エレメントを生成したりといったコストを避けるために、あらかじめ1つだけエレメントを用意しておいて、それを
cloneNode()
することで賄う、というような手法が定石となっていて、jQueryでもclone()
メソッドがあります。コピーしないほうが…速い!?
ところが、jsPerfで測定していたところ、奇妙な現象が起きました。なんと、
clone()
せずに、都度HTMLからjQueryオブジェクトを生成するほうが速くなってしまったのです。jQueryのソースコードを追ってみると、HTMLからDOMを生成する
$.parseHTML()
で、<foo />
や<foo></foo>
のような空タグの場合は正規表現で振り分けて、document.createElement()
に投げるようになっており、これでHTMLのパースをバイパスしているので速かったのでした。もちろん、そんなものもすべてかっ飛ばして、自力でdocument.createElement()
したものをjQueryに変換すればもっと高速になります。jQueryを使っていても、生DOMの知識があれば、さらに使いこなすことができます。clone()の遅さの要因
そこで、今度は
clone()
だけの速度を測定してみました。今回は、1エレメントだけのものと、いくつかのエレメントが入ったものについて、- jQueryの
.clone()
を使用 - DOMの
cloneNode()
を使った上で、jQueryに変換 - (参考)DOMの
cloneNode()
だけの速度
を比較してみました。2番目と3番目の差はjQueryオブジェクト生成のコスト、そして1番目と2番目の差は
.clone()
のオーバーヘッド、ということになります。測定結果からわかるように、
.clone()
は単純にcloneNode()
したものをjQueryに変換した場合と比べて、10倍以上の時間を要しています。ソースから読み取れることとしては、- jQueryオブジェクトが複数エレメントでもちゃんとcloneできるように処理を開く
- 古いIEなどで、フォーム要素の状態や、HTML5で追加になったタグがうまくコピーされない問題の解消
- scriptタグを検索しての特別処理
- jQuery内部で持っている、イベントやデータのコピー
などの処理が加わっています。とはいえ、テンプレートをコピーするような場面では、scriptタグはまず現れないでしょうし、動的に生成する要素で全てにイベントを割り当てるのであれば、そうするより
.on()
で拾うべき場面でしょう。このような問題がない、親要素1つの中に全て入ったようなテンプレートであれば、jQueryの.clone()
を使うよりネイティブにcloneNode()
してからjQueryオブジェクトにしたほうが格段に速いことを、知っておいて損はないと思います(もちろん、100個もコピーしないのであれば、速度向上と言っても微々たるもので、そこまでの意味は持たないでしょうが)。