strings vs array join vs doc fragment

JavaScript performance comparison

Revision 8 of this test case created by

Preparation code

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<style>
  #list li{float:left}
</style>
<ul id="list">
</ul>
      
<script>
Benchmark.prototype.setup = function() {
  // doT.js
  // 2011, Laura Doktorova
  // https://github.com/olado/doT
  //
  // doT is a custom blend of templating functions from jQote2.js
  // (jQuery plugin) by aefxx (http://aefxx.com/jquery-plugins/jqote2/)
  // and underscore.js (http://documentcloud.github.com/underscore/)
  // plus extensions.
  //
  // Licensed under the MIT license.
  //
  (function() {
    var doT = {
      version: '0.1.6'
    };
  
    if (typeof module !== 'undefined' && module.exports) {
      module.exports = doT;
    } else {
      this.doT = doT;
    }
  
    doT.templateSettings = {
      evaluate: /\{\{([\s\S]+?)\}\}/g,
      interpolate: /\{\{=([\s\S]+?)\}\}/g,
      encode: /\{\{!([\s\S]+?)\}\}/g,
      use: /\{\{#([\s\S]+?)\}\}/g,
      //compile time evaluation
      define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,
      //compile time defs
      varname: 'it',
      strip: true,
      append: true
    };
  
    function resolveDefs(c, block, def) {
      return ((typeof block === 'string') ? block : block.toString()).replace(c.define, function(match, code, assign, value) {
        if (code.indexOf('def.') === 0) {
          code = code.substring(4);
        }
        if (!(code in def)) {
          if (assign === ':') {
            def[code] = value;
          } else {
            eval("def[code]=" + value);
          }
        }
        return '';
      }).replace(c.use, function(match, code) {
        var v = eval(code);
        return v ? resolveDefs(c, v, def) : v;
      });
    }
  
    doT.template = function(tmpl, c, def) {
      c = c || doT.templateSettings;
      var cstart = c.append ? "'+(" : "';out+=(",
           // optimal choice depends on platform/size of templates
          cend = c.append ? ")+'" : ");out+='";
      var str = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl;
  
      str = ("var out='" + ((c.strip) ? str.replace(/\s*<!\[CDATA\[\s*|\s*\]\]>\s*|[\r\n\t]|(\/\*[\s\S]*?\*\/)/g, '') : str).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(c.interpolate, function(match, code) {
        return cstart + code.replace(/\\'/g, "'").replace(/\\\\/g, "\\").replace(/[\r\t\n]/g, ' ') + cend;
      }).replace(c.encode, function(match, code) {
        return cstart + code.replace(/\\'/g, "'").replace(/\\\\/g, "\\").replace(/[\r\t\n]/g, ' ') + ").toString().replace(/&(?!\\w+;)/g, '&#38;').split('<').join('&#60;').split('>').join('&#62;').split('" + '"' + "').join('&#34;').split(" + '"' + "'" + '"' + ").join('&#39;').split('/').join('&#47;'" + cend;
      }).replace(c.evaluate, function(match, code) {
        return "';" + code.replace(/\\'/g, "'").replace(/\\\\/g, "\\").replace(/[\r\t\n]/g, ' ') + "out+='";
      }) + "';return out;").replace(/\n/g, '\\n').replace(/\t/g, '\\t').replace(/\r/g, '\\r').split("out+='';").join('').split("var out='';out+=").join('var out=');
  
      try {
        return new Function(c.varname, str);
      } catch (e) {
        if (typeof console !== 'undefined') console.log("Could not create a template function: " + str);
        throw e;
      }
    };
  }());
  var Template = function(id) {
      this.r = doT.template(document.getElementById('must').innerHTML);
      this.o = [];
      this.add = function(data) {
        this.o.push(this.r(data));
      }
      this.getOutput = function() {
        return this.o.join('');
      }
      };
  var d = {
    first: 'lorem',
    second: 'ipsum'
  };

};

Benchmark.prototype.teardown = function() {
  document.getElementById('list').innerHTML = '';

};
</script>

Preparation code output

<style> #list li{float:left} </style> <ul id="list"> </ul>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Strings
var str = '';
var c = '<li>' + d.first + ' ' + d.second + '</li>'
for (var i = 0; i < 100; i++) {
  str += c;
}
document.getElementById('list').innerHTML = str;
pending…
Array (using push)
var arr = [];
var c = '<li>' + d.first + ' ' + d.second + '</li>'
for (var i = 0; i < 100; i++) {
  arr.push(c);
}
document.getElementById('list').innerHTML = arr.join('');
pending…
Doc Fragment
var doc = document.createDocumentFragment();
var c = d.first + ' ' + d.second;

for (var i = 0; i < 100; i++) {
  var e = document.createElement("li");
  e.textContent = c;
  doc.appendChild(e);
}
document.getElementById('list').appendChild(doc);
pending…
Array (using index)
var arr = [];
var c = '<li>' + d.first + ' ' + d.second + '</li>'
for (var i = 0; i < 100; i++) {
  arr[i] = c;
}
document.getElementById('list').innerHTML = arr.join('');
pending…
Array join then string concat
var arr = [];
var c = d.first + ' ' + d.second;

for (var i = 0; i < 100; i++) {
  arr[i] = c;
}
document.getElementById('list').innerHTML = '<li>' + arr.join('</li><li>') + '</li>';
pending…
DOM List
var list = document.getElementById('list');
var c = d.first + ' ' + d.second;

for (var i = 0; i < 100; i++) {
  var e = document.createElement("li");
  e.textContent = c;
  list.appendChild(e);
}
pending…
jquery
var $list = $("#list");
var c = '<li>' + d.first + ' ' + d.second + '</li>';
for (var i = 0; i < 100; i++) {
  $list.append(c);
}
pending…
doc fragment 2
var doc = document.createDocumentFragment();
var c = d.first + ' ' + d.second;
var e = document.createElement("li");
e.textContent = c;
for (var i = 0; i < 100; i++) {
  doc.appendChild(e.cloneNode(true));
}
document.getElementById('list').appendChild(doc);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments