strings vs array join vs doc fragment (v12)

Revision 12 of this benchmark created by ~adi on


Description

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

Preparation HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js">
</script>
<style>
  #list li{float:left}
</style>
<ul id="list">
</ul>

Setup

// 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'
    };

Teardown


    document.getElementById('list').innerHTML = '';
  

Test runner

Ready to run.

Testing in
TestOps/sec
Strings
var str = '';
var c = '<li>' + d.first + ' ' + d.second + '</li>'
for (var i = 0; i < 1000; i++) {
  str += c;
}
document.getElementById('list').innerHTML = str;
ready
Array (using push)
var arr = [];
var c = '<li>' + d.first + ' ' + d.second + '</li>'
for (var i = 0; i < 1000; i++) {
  arr.push(c);
}
document.getElementById('list').innerHTML = arr.join('');
ready
Doc Fragment
var doc = document.createDocumentFragment();
var c = d.first + ' ' + d.second;

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

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

for (var i = 0; i < 1000; i++) {
  var e = document.createElement("li");
  e.textContent = c;
  list.appendChild(e);
}
ready
jquery
var $list = $("#list");
var c = '<li>' + d.first + ' ' + d.second + '</li>';
for (var i = 0; i < 1000; i++) {
  $list.append(c);
}
ready
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 < 1000; i++) {
  doc.appendChild(e.cloneNode(true));
}
document.getElementById('list').appendChild(doc);
ready
ccfrag
var a = document.createDocumentFragment();
var b = d.first + ' ' + d.second;
for (var i = 0; i < 1000; i++) {
  a.appendChild(document.createElement("li")).textContent = b;
}
document.getElementById('list').appendChild(a);
ready
array with docfrag
var doc = document.createDocumentFragment();
var arr = [];
var c = '<li>' + d.first + ' ' + d.second + '</li>'
var e = document.createElement('tempElement');
for (var i = 0; i < 1000; i++) {
  arr[i] = c;
}
e.innerHTML = arr.join('');
doc.appendChild(e);
document.getElementById('list').appendChild(e);
ready

Revisions

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