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文字列の単純な結合
テスト 2TKStringBuffer を用いた文字列の結合

私の手元の環境は 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 を常に使っておくことが無難な実装といえそうです。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説