JavaScript の文字列の結合を最適化する
Ajax プログラミングでは、サーバーから返されたデータを用いて HTML 要素 (テーブル等) を構築し、 それをあらかじめ用意されたプレースホルダー (div や span 要素など) に差し込むことがしばしば行われます。 特に JavaScript で文字列を結合する箇所についてどのように実装すれば、高速に処理可能であるか試しましたので、 この資料ではそのテスト方法と結果を示します。
2019年現在、この記事で記載のパフォーマンス上の問題は無視できる程度で、ことさら問題にする必要もないと考えています。 歴史的な意味で、書き残しておきます。
実験準備 (ラッパークラスの作成)
当サイトの資料 Ajax プログラミング入門 でもちらっと紹介しましたが、 文字を結合する際には Array に文字列を push していき、最後に join('') によって結合するのが良いとの情報がありましたので、 その真偽を調べようと考えました。比較対象は単純な文字列の結合です。
Array はそのまま使うのではなく、薄いラッパーを書きました。TKStringBuffer というクラス名にし、push 操作を append という名のメソッドに置き換えています。また、join('') は JavaScript の流儀に従って toString() メソッドに置き換えています。TKStringBuffer の実装は下記の通りです。
function TKStringBuffer() { this.__buffer = new Array(); } TKStringBuffer.prototype = { clear: function() { this.__buffer = new Array(); }, append: function(s) { this.__buffer.push(s); }, appendn: function(s) { this.append(s); this.append('\n'); }, toString: function() { return this.__buffer.join(''); }, /* Alias */ a: function(s) { this.append(s); }, an: function(s) { this.appendn(s); } };
実験: + 演算子による文字列の結合 vs Array.push, push, ..., Array.join('')
実験は以下のように文字列を指定回数足し算する場合と、Array.push を指定回数繰り返し最後に join('') する場合を比較します。
尚、TKStringBuffer のように Array() を明示的に new して、使う以外に
var a = [ 'a', 'b', 'c' ];
のようにして配列を作成する方法もあります。
以前は prototype.js を使っていましたが jQuery で書き直しています。
$(document).ready(function() { $('#btn1').on('click', function(evt) { console.log('btn1 clicked'); var cnt = get_cnt(); var txt = get_teststr(); var start = new Date().getTime(); var s = ''; for (var i = 0; i < cnt; i++) { s += txt; } var start2 = new Date().getTime(); $('#str').html(s); var end = new Date().getTime(); $('#result_1a').html(end - start); $('#result_1b').html(start2 - start); $('#result_1c').html(end - start2); $('#result_1d').html(s.length); }); $('#btn2').on('click', function(evt) { console.log('btn2 clicked'); var cnt = get_cnt(); var txt = get_teststr(); var start = new Date().getTime(); var s = new TKStringBuffer(); for (var i = 0; i < cnt; i++) { s.a(txt); } var start2 = new Date().getTime(); $('#str').html(s.toString()); var end = new Date().getTime(); $('#result_2a').html(end - start); $('#result_2b').html(start2 - start); $('#result_2c').html(end - start2); $('#result_2d').html(s.toString().length); }); function get_cnt() { return parseInt($('#cnt').val()); } function get_teststr() { return $('#txt').val(); } });
以下のフォームを使って、実際に試していただけます。
結合回数: | ||||||
結合文字: | ||||||
テスト# | 説明 | 結果 | ||||
所要時間[ms] | 結合[ms] | innerHTML 及び Array.join[ms] | 文字列長 | |||
テスト 1 | 文字列の単純な結合 | |||||
テスト 2 | TKStringBuffer を用いた文字列の結合 |
私の手元の環境は Windows Vista SP1 で、ブラウザは Internet Explorer 7 と Firefox 3 です。
結果: Array.push, push, ... , join('') の圧勝
実験結果は以下です。Firefox は常に高速。Internet Explorer は文字列の結合が極端に遅いことが確認できました。
Firefox では Array.push を使用した場合、逆にわずかに文字列の足し算より遅くなるようです。しかしその差はごくわずか (1000回の足し算で 1~2ms 程度) で、目くじら立てるほどのことはありません (ここを気にしてあれこれ手を打つくらいなら、 他にやることはあります)。
Internet Explorer では文字列の足し算が極端に遅く、Firefox ではどちらでもほぼ同じであることを考えると、 文字列は足し算するのではなく、Array.push と join を常に使っておくことが無難な実装といえそうです。