strings vs array join vs doc fragment

JavaScript performance comparison

Revision 8 of this test case created

Info

Testing performance of building a string and appending as innerHTML vs creating a documentFragmente and appending it as DOM.

Preparation code

<script src="//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

Test runner

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

Java applet disabled.

Testing in unknown unknown
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. Here’s a list of current revisions for this page:

0 comments

Add a comment