DOM functions v.s. innerHTML v.s. cloneNode

JavascriptでDOM要素を生成するにはいろいろなやり方があるけど,どれが一番速いのか気になっていた。漠然と,cloneNode速そうだなーと思ってそれを使っていた。
思い込みで速度を語るのはよくない!と思ったので,Firefox3 RC1 + Firebug 1.2.0b1 で軽くベンチマークを取ってみた。
ベンチマークに利用したソースと,結果を貼り付けてみる。

シンプルなHTMLを生成した場合

<div><a href="http://example.com">Example</a></div>

こんなHTMLを,

  1. document.createElement, document.createTextNode, elem.appendChild を使って生成した場合
  2. div を生成して innerHTML に内容を代入する場合
  3. あらかじめ生成してある要素を cloneNode(true) してコピーする場合

の3つの方法で生成した場合の速度を測定してみた。

Javascriptソース
var simple_elem = document.createElement('div');
simple_elem.innerHTML = '<a href="http://example.com/">Example</a>';

function simple_DOM() {
    var div = document.createElement('div');
    var a = document.createElement('a');
    a.appendChild(document.createTextNode('Example'));
    div.appendChild(a);
}
function simple_innerHTML() {
    var div = document.createElement('div');
    div.innerHTML = '<a href="http://example.com/">Example</a>';
}
function simple_cloneNode() {
    var div = simple_elem.cloneNode(true);
}

const n = 10000;

console.profile();
for(var i = 0; i < n; i++)
    simple_innerHTML();
for(var i = 0; i < n; i++)
    simple_DOM();
for(var i = 0; i < n; i++)
    simple_cloneNode();
console.profileEnd();
結果
Function Calls Percent Own Time Time Avg Min Max
simple_DOM 10000 36.15% 384.087ms 384.087ms 0.038ms 0.034ms 2.134ms
simple_innerHTML 10000 49.02% 520.853ms 520.853ms 0.052ms 0.048ms 1.514ms
simple_cloneNode 10000 14.83% 157.589ms 157.589ms 0.016ms 0.013ms 2.876ms
(?)() 6 0% 0.051ms 0.051ms 0.008ms 0.005ms 0.022ms

複雑なHTMLを生成した場合

<table>
<tbody>
<tr><th>aaa</th><td>hoge</td></tr>
<tr><th>bbb</th><td>hoge</td></tr>
</tbody>
</table>

こんなHTMLを先ほどと同じやり方で生成してみた。

Javascriptソース
var complex_elem = document.createElement('table');
complex_elem.innerHTML = '<tbody><tr><th>aaa</th><td>hoge</td></tr><tr><th>bbb</th><td>hoge</td></tr></tbody>';

function complex_DOM() {
    var table = document.createElement('table');
    var tr_a = document.createElement('tr');
    var th_a = document.createElement('th');
    th_a.appendChild(document.createTextNode('aaa'));
    tr_a.appendChild(th_a);
    var td_a = document.createElement('td');
    td_a.appendChild(document.createTextNode('hoge'));
    tr_a.appendChild(td_a);
    var tr_b = document.createElement('tr');
    var th_b = document.createElement('th');
    th_b.appendChild(document.createTextNode('bbb'));
    var td_b = document.createElement('td');
    td_b.appendChild(document.createTextNode('hoge'));
    tr_b.appendChild(th_b);
    tr_b.appendChild(td_b);
    table.appendChild(tr_a);
    table.appendChild(tr_b);
}
function complex_innerHTML() {
    var table = document.createElement('table');
    table.innerHTML = '<tbody><tr><th>aaa</th><td>hoge</td></tr><tr><th>bbb</th><td>hoge</td></tr></tbody>';
}
function complex_cloneNode() {
    var table = complex_elem.cloneNode(true);
}

const n = 10000;

console.profile();
for(var i = 0; i < n; i++)
    complex_innerHTML();
for(var i = 0; i < n; i++)
    complex_DOM();
for(var i = 0; i < n; i++)
    complex_cloneNode();
console.profileEnd();
結果
Function Calls Percent Own Time Time Avg Min Max
complex_DOM 10000 57.84% 1467.06ms 1467.06ms 0.147ms 0.129ms 20.188ms
complex_innerHTML 10000 30.62% 776.493ms 776.493ms 0.078ms 0.068ms 6.842ms
complex_cloneNode 10000 11.54% 292.589ms 292.589ms 0.029ms 0.019ms 47.847ms
(?)() 6 0% 0.05ms 0.05ms 0.008ms 0.004ms 0.022ms

考察

結果としては,予想通り。cloneNodeが速かった。DOM関数を使う場合には関数呼び出しのオーバーヘッドがあるだろうし,innerHTMlにはHTMLのパースをする分だけ時間がかかることを考えれば,まあ当然な結果だろう。ソースの長さを見る限り,DOM関数は遅くなっても当たり前,という感じすらする。

まとめ

似たような要素をたくさん生成するときはcloneNode使うのがいいよ。
とはいえ,全く同じ要素をたくさん生成することはまれだろう。次回は,内容が微妙に異なる,似たような要素をたくさん生成する場合にどうなるかを実験してみたい。