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
A popular strategy for flow control in JavaScript is to use a list of closures and call one closure with the results of the previous closure, as in the delightful little Step
library by Tim Caswell.
Step(
function readSelf() {
fs.readFile(__filename, this);
},
function capitalize(err, text) {
if (err) throw err;
return text.toUpperCase();
},
function showIt(err, newText) {
if (err) throw err;
console.log(newText);
}
);
I was wondering if how the creation of a handful of closures at function invocation would compare to the use of an object, where the steps would be member functions in the object, and state would be preserved in this
.
These tests are not asynchronous. They simply wrap a two step operation in helper functions. The first step pops an element from an array. The second step sums the remaining array elements.
I encountered an interesting case with Chrome 19 where assigning one of the closures functions to an object property of any object in the enclosing function causes significant drop in performance, so there are additional tests to illustrate that case.
var y = {};
var test = (function() {
var test = {};
test.closure = function closure(array) {
var popper, summer;
popper = function() {
array.pop();
return summer();
}
summer = function() {
var i, I, sum = 0;
for (i = 0, i < array.length; i < I; i++) sum += array[i];
return sum;
}
return popper();
}
test.objectAssignedUncalled = function objectAssignedUncalled(array) {
var x = {};
x.popper = function() {
array.pop()
};
function helper() {
var i, I, sum = 0;
for (i = 0, I = array.length; i < I; i++) sum += array[i];
return i;
}
return helper();
}
test.objectAssigned = function objectAssigned(array) {
var z = {};
z.popper = function () {
array.pop();
return helper();
};
return z.popper();
function helper() {
var i, I, sum = 0;
for (i = 0, I = array.length; i < I; i++) sum += array[i];
return i;
}
}
test.objectAssignedViaVar = function objectAssignedViaVar(array) {
var z = {}, popper = function () {
array.pop();
return summer();
};
z.popper = popper;
return z.popper();
function summer() {
var i, I, sum = 0;
for (i = 0, I = array.length; i < I; i++) sum += array[i];
return i;
}
}
test.hoisted = function hoisted(array) {
function popper() {
array.pop();
return summer();
}
function summer() {
var i, I, sum = 0;
for (i = 0, i < array.length; i < I; i++) sum += array[i];
return sum;
}
return popper();
}
function Proto() {}
Proto.prototype.popper = function(array) {
this.array = array;
array.pop();
return this.summer();
}
Proto.prototype.summer = function() {
var array = this.array,
sum = 0,
i, I;
for (i = 0, I = array.length; i < I; i++) sum += array[i];
return sum;
}
test.proto = function proto(array) {
return (new Proto()).popper(array);
}
Proto.cache = [];
test.cachedProto = function cachedProto(array) {
var proto = Proto.cache.length ? Proto.cache.pop() : (new Proto()),
sum = proto.popper(array);
Proto.cache.push(proto);
return sum;
}
var popper, summer;
test.outerAssigned = function outerAssigned(array) {
popper = function () {
array.pop();
return summer();
}
summer = function () {
var i, I, sum = 0;
for (i = 0, i < array.length; i < I; i++) sum += array[i];
return sum;
}
return popper();
}
return test;
})();
Ready to run.
Test | Ops/sec | |
---|---|---|
Closures. |
| ready |
Hoisted closures. |
| ready |
Prototype. |
| ready |
Cached prototype. |
| ready |
Closure assigned to object property. |
| ready |
Closure assigned to object property, not called. |
| ready |
Closures assigned to outer variables. |
| ready |
Closure assigned to object property via variable. |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.