意外と遅い、jQuery#clone | 電脳想

電脳想

デジタル社会の中で考え思うこと

この記事は、jQuery Advent Calendar 2014の4日目です。

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エレメントだけのものと、いくつかのエレメントが入ったものについて、

  1. jQueryの.clone()を使用

  2. DOMのcloneNode()を使った上で、jQueryに変換

  3. (参考)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個もコピーしないのであれば、速度向上と言っても微々たるもので、そこまでの意味は持たないでしょうが)。