html encode
JavaScript performance comparison
Info
Speed test comparing different methods of escaping html.
A loop with an if-statement using char-codes is the fastest (or almost so) method in all browsers. (entry "loop + if-charcode; append")
Preparation code
<pre id="output_vis"></pre>
<script>
var docTxts = [];
window.onload = function() {
var docTxt = document.documentElement.innerHTML.substr(0,20000);
do{
docTxts.push(docTxt);
docTxt = docTxt.substr(0,Math.floor(docTxt.length*0.6));
} while(docTxt.length>1);
};
</script>
<script>
Benchmark.prototype.setup = function() {
function encodeWithIfs(str) {
var result = ""
for (var cc = 0; cc < str.length; cc++) {
var c = str.charAt(cc)
if (c === "&") result += '&'
else if (c === '"') result += '"'
else if (c === "'") result += '''
else if (c === "<") result += '<'
else if (c === ">") result += '>'
else result += c
}
return result
}
function endcodeWithRegEx(str) {
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>')
}
function endcodeWithRegEx2(str) {
return str.replace(/[&<>"'`]/g, function (chr) {
return '&#' + chr.charCodeAt(0) + ';'
})
}
function encodeWithSwitch(str) {
var c, result = "", length = str.length;
for (var i = 0; i < length; i++) {
switch(c = str.charAt(i)) {
case "&": c = "&"; break;
case '"': c = """; break;
case "'": c = "'"; break;
case "<": c = "<"; break;
case ">": c = ">"; break;
}
result += c;
}
return result;
}
var encodeWithObjectProps = function(){
var map = {'"':'"','&':'&',"'":''','<':'<','>':'>'};
return function (str) {
var result = "", len = str.length, doneUpto = 0;
for (var i=0; i < len ; i++) {
var char = str.charAt(i);
var ref = map[char];
if(ref === undefined) continue;
result += doneUpto === i ? ref : str.substring(doneUpto, i) + ref;
doneUpto = i + 1;
}
if(doneUpto !== len) result += str.substring(doneUpto, len);
return result;
}
}();
function encodeByCharCodeJoin(str) {
var res = [], len = str.length, doneUpto = 0;
for (var i=0; i < len; i++) {
var c, code = str.charCodeAt(i);
if(code > 62) continue;
else if(code === 38) c = "&";
else if(code === 34) c = """;
else if(code === 39) c = "'";
else if(code === 60) c = "<";
else if(code === 62) c = ">";
else continue;
if(doneUpto !== i) res.push(str.substring(doneUpto, i));
res.push(c);
doneUpto = i+1;
}
if(doneUpto !== len) res.push(str.substring(doneUpto, len));
return res.join("");
}
function encodeByCharCodeAppend(str) {
var result = "", len = str.length, doneUpto = 0;
for (var i=0; i < len ; i++) {
var ref, code = str.charCodeAt(i);
if(code > 62) continue; //fast-path
else if(code === 38) ref = "&";
else if(code === 34) ref = """;
else if(code === 39) ref = "'";
else if(code === 60) ref = "<";
else if(code === 62) ref = ">";
else continue;
result += doneUpto === i ? ref : str.substring(doneUpto, i) + ref;
doneUpto = i + 1;
}
if(doneUpto !== len) result += str.substring(doneUpto, len);
return result;
}
var encodeByCharCodeLookup = (function(){
var map = {'"':'"','&':'&',"'":''','<':'<','>':'>'};
var lookup = [];
for(var j=0;j<63;j++) lookup[j] = map[String.fromCharCode(j)];
return function (str) {
var result = "", len = str.length, doneUpto = 0;
for (var i=0; i < len ; i++) {
var code=str.charCodeAt(i);
if(code>62) continue;
var ref = lookup[code];
if(ref === undefined) continue;
result += doneUpto === i ? ref : str.substring(doneUpto, i) + ref;
doneUpto = i + 1;
}
if(doneUpto !== len) result += str.substring(doneUpto, len);
return result;
};
})();
var encodeViaDom = (function(){ //limited: doesn't encode ' and "
var el = document.createElement('div');
return function(val) {
el.textContent = val;
return el.innerHTML;
};
})();
var encodeWithRegExpAndObject = (function() {
var o={"&":"&", '"':""", "'":"'", "<":"<", ">":">"};
function mapchar(chr) { return o[chr]; }
return function (str) { return str.replace(/[&"'<>]/g, mapchar); };
})();
//buggy, doesn't work in IE10, not in chrome 23, and not in FF20
function encodeWithStringPrototype(str) {
return ''.link(str).slice(9,-6).replace(/&/g, '&');
}
function doEncode(func) {
//ensure JIT doesn't omit call entirely by storing output in an observable location. Also, use this to debug...
window.output_sink = [
func("All replacement chars & \" ' < > "+docTxts[0].length)
];
for(var i =0;i<docTxts.length;i++) {
var iters = Math.sqrt(docTxts[0].length / docTxts[i].length);
for(var j=0;j<iters;j++)
window.output_sink.push(func(docTxts[i]));
}
}
};
Benchmark.prototype.teardown = function() {
document.getElementById('output_vis').textContent = window.output_sink[0];
};
</script>
Preparation code output
Test runner
Warning! For accurate results, please disable Firebug before running the tests. (Why?)
Java applet disabled.
| Test | Ops/sec | |
|---|---|---|
loop + if-charAt |
|
pending… |
multiple regexes |
|
pending… |
regex + match func |
|
pending… |
loop + object lookup |
|
pending… |
regex + object lookup |
|
pending… |
using DOM innerHtml |
|
pending… |
loop + if-charcode; join |
|
pending… |
loop + if-charcode; append |
|
pending… |
loop + charcode-lookup; append |
|
pending… |
loop + switch-charAt |
|
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:
- Revision 1: published by Dan Rzeppa
- Revision 2: published by Dan Rzeppa
- Revision 3: published by WebReflection
- Revision 4: published by Johan Sundström
- Revision 5: published
- Revision 8: published by Eamon Nerbonne
- Revision 9: published by Eamon Nerbonne
- Revision 10: published by Dan Rzeppa
- Revision 11: published by Dan Rzeppa
0 comments