jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<div></div>
<div id="foo"></div>
<div class="bar"></div>
<script>
var selecthash = {
"#": "getElementById",
".": "getElementsByClassName"
};
function select2(selector, context) {
var c = selector.charAt(0),
method = selecthash[c];
if (method) {
selector = selector.substr(1);
} else {
method = "getElementsByTagName"
}
return (context || document)[method](selector);
}
function select(selector, context) {
var c = selector.charAt(0),
method;
if (c === "#") {
method = "getElementById";
selector = selector.substr(1);
} else if (c === ".") {
method = "getElementsByClassName";
selector = selector.substr(1);
} else {
method = "getElementsByTagName";
}
return (context || document)[method](selector);
}
var By = {
id: function _byId(id) {
return document.getElementById(id);
},
tag: function _byTag(tag, context) {
return (context || document).getElementsByTagName(tag);
},
"class": function _byClass(klass, context) {
return (context || document).getElementsByClassName(klass);
}
};
function select3(selector, context) {
var c = selector.charAt(0),
context = context || document;
if (c === '#') {
return context.getElementById(selector.substr(1));
} else if (c === '.') {
return context.getElementsByClassName(selector.substr(1));
} else {
return context.getElementsByTagName(selector);
}
}
var byId = By.id;
var byClass = By.class;
var byTag = By.tag;
var byId2 = document.getElementById.bind(document);
</script>
/**
@file qs.js
Query Selector
== Terminology ==
Query: Comma-separated group of selectors (a.nav, button)
Compound: Cluster of simple selectors (a#login.onsite)
Chain: Group of compounds separated by combinators.
Combinator: Separates compounds. Currently only whitespace.
*/
"use strict";
/**
qs
Perform a simple query selection.
@param String query
@param Node root
optional root node, defaults to `document`.
@return Array<Node>
*/
function qs (query, root){
return (qs.cache[query] || qs.compile(query))(root || document);
};
qs.cache = {};
qs.A = [];
qs.a = function (obj) {
return qs.A.slice.call(obj, 0);
};
qs.hasClass = function (node, v) {
return (' ' + node.className + ' ').indexOf(' ' + v + ' ') > -1;
};
qs.check = function (node, cmp) {
if (node == this.doc) return false;
var cn, i = 0;
if (cmp.id && (cmp.id != node.id)) return false;
if (cmp.tn && (cmp.tn != node.tagName)) return false;
if (cmp.cn) while (cn = cmp.cn[i++])
if (!qs.hasClass(node, cn)) return false;
return true;
};
qs.go = function (root, doc) {
return new qs.Selection(root, doc);
};
// regexen
qs.r = {
singletons: /^(?:body|head|title)$/i,
cn: /\.[^\s\.#]+/g,
id: /#[^\s\.#]+/g,
tn: /^[^\s\.#]+/g,
lTrim: /^\s\s*/,
rTrim: /\s\s*$/,
comma: /\s*,\s*/,
space: /\s+/
};
// regex madness
qs.compile = function (query) {
var fn, id, out = '', q = new qs.Query(query),
selectors = q.selectors,
selector = selectors[0],
compound, lastChain, i = -1;
if (qs.cache[q]) {
return qs.cache[query] = qs.cache[q];
}
if (selectors.length > 1) {
return qs.cache[q] = qs.cache[query] = new Function(
'root', 'return qs.combine(\n qs("'
+ selectors.join('", root),\n qs("') + '", root)\n);'
);
}
out = '\nvar doc = root.ownerDocument || root;'
+ '\nvar nextRoot;'
+ '\nvar lastAncestor;'
+ '\nvar nodes = [root];';
lastChain = 0;
while (compound = selector[++i]) {
if (compound.id ||
qs.r.singletons.test('' + compound.tn) ||
(i == selector.length - 1)) {
out += qs.compile.compoundToChain(
compound, selector.slice(lastChain, i)
);
lastChain = i + 1;
}
}
out += '\nreturn nodes || [];'
return qs.cache[query] = qs.cache[q] = new Function('root', out);
}
qs.compile.compoundToLiteral = function (cmp) {
var cn = cmp.cn.join('","');
return '{' +
( (cmp.tn ? ',tn:"' + cmp.tn + '"' : '')
+ (cmp.id ? ',id:"' + cmp.id + '"' : '')
+ (cn ? ',cn:["' + cn.substring(1) + '"]' : '')
).substring(1) + '}';
};
qs.compile.compoundToChain = function (cmp, ancestorChecks) {
var out, check, hasId,
exit = '\nif (!nodes.length) return [];';
cmp = cmp.copy();
if (qs.r.singletons.test('' + cmp.tn)) {
out = '\nroot = (nodes = root.getElementsByTagName("'
+ cmp.tn + '"))[0];' + exit;
cmp.tn = false;
}
else if (cmp.id) {
out = '\nnodes = [doc.getElementById("'
+ cmp.id.substring(1) + '")];'
hasId = true;
cmp.id = false;
}
else if (cmp.cn.length) {
out = '\nnodes = qs.getByClass(nodes[0], "'
+ cmp.cn.shift().substring(1) + '");';
}
else if (cmp.tn) {
out = '\nnodes = root.getElementsByTagName("'
+ cmp.tn + '");' + exit;
cmp.tn = false;
}
if (cmp.id || cmp.tn || cmp.cn && cmp.cn.length) {
out += exit + '\nnodes = qs.filter(nodes, '
+ qs.compile.compoundToLiteral(cmp) + ');';
}
if (ancestorChecks.length) {
out += exit + '\nnodes = qs.checkAncestors(nodes, root,';
while (check = ancestorChecks.shift()) {
out += qs.compile.compoundToLiteral(check);
if (ancestorChecks.length) out += ', ';
}
out += ');';
}
if (hasId) {
out += exit + '\nroot = (nodes = [qs.checkRoot(nodes, root)])[0];';
}
return out;
};
qs.Compound = function (s) {
this.cn = s.match(qs.r.cn) || [];
this.id = (s.match(qs.r.id) || [])[0] || '';
this.tn = ((s.match(qs.r.tn) || [])[0] || '').toUpperCase();
this.cn.sort();
};
qs.Compound.prototype.copy = function () {
return {cn: this.cn.slice(), id:this.id, tn:this.tn};
};
qs.Compound.prototype.toString = function () {
if (this.normalized) return this.normalized;
return this.normalized = this.tn + this.id + this.cn.join('');
};
qs.Query = function (selector) {
var selector, selectors = selector.split(qs.r.comma),
compound, compounds, i = -1, j;
this.original = selector;
while (selector = selectors[++i]) {
selector = selector.replace(qs.r.lTrim, '').replace(qs.r.rTrim, '');
compounds = selector.split(qs.r.space);
j = -1;
while (compound = compounds[++j]) {
compounds[j] = new qs.Compound(compound);
}
selectors[i] = compounds;
}
this.selectors = selectors.sort();
}
qs.Query.prototype.toString = function () {
if (this.normalized) return this.normalized;
var selector, selectors = this.selectors, i = -1, out = '';
while (selector = selectors[++i]) {
if (i) out += ', ';
out += selector.join(' ');
}
return this.normalized = out;
}
// generated code calls these
qs.getByClass = function (root, v) {
return root.getElementsByClassName(v);
};
qs.filter = function (nodes, cmp) {
var node, result = [], i = 0;
while (node = nodes[i++]) {
if (!qs.check(node, cmp)) continue;
result.push(node);
}
return result;
};
qs.checkAncestors = function (nodes, root) {
var node, result = [], ancestors = [], ancestor,
argv, argc = arguments.length, argi = argc,
lastAncestor, i = 0;
if (argc > 2) {
result = [];
while (node = nodes[i++]) {
ancestor = node;
argv = arguments[--argi];
while ((ancestor = ancestor.parentNode)) {
if (qs.check(ancestor, argv)) {
argv = arguments[--argi];
if (argi < 2) {
if (!lastAncestor) lastAncestor = ancestor;
result.push(node);
break;
}
}
if (ancestor == root) {
break;
}
}
argi = argc;
}
}
this.lastAncestor = lastAncestor;
return result;
};
qs.checkRoot = function (root, node) {
if (!root.ownerDocument) return node;
while ((node = node.parentNode)) {
if (node == root) return node;
}
return false;
};
// TODO: remove me
window.qs = qs;
Ready to run.
Test | Ops/sec | |
---|---|---|
native |
| ready |
select |
| ready |
QSA |
| ready |
By |
| ready |
jQuery |
| ready |
select2 |
| ready |
select3 |
| ready |
local |
| ready |
local2 |
| ready |
qs |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.