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
Why is map so much slower then a while loop?
We can compare Array.prototype.map the specification as implemented here on mdn(judging by IE9 performance I don't think it's an accurate implementation for all browsers) and a quickMap function that just uses a basic while loop.
If we play around with specification map we can see that checking if the key is there (i.e. treating it like a sparse array) is the biggest slowdown in chrome, doubling the speed, the other big bottle neck is using function.call every time.
If we only use function.call when thisArg is defined, and don't bother to check if all keys are there then we can boost our speed back up to quickMap speeds.
Yay!, except the spec is pretty clear that this needs to be undefined if it isn't specified
Ok so we can create a version that binds it once right to avoid creating the new closure so meany times right ? WRONG! Chrome hates bind. Best I could come up with that didn't trash the speak was to create an empty object and call the function as a method of that, not spec but close.
Next up if it is called on a sparse array the function should only be called on defined keys, why don't we just use object keys to grab the keys that exist? WRONG! just checking once slows down Chrome somewhat and hoses IE9. I can't think of another way to check for array sparseness, and as we can also see checking if a key is defined doesn't make a difference.
So it seems to me if your not using this and not using a sparse array your better off rolling your own map.
var _len = 10000;
var a = new Array(_len);
for (var i = 0; i < _len; i++) {
a[i] = i;
}
var f = function(a) {
return a * a;
}
Array.prototype.qMap = function(fun) {
var len = this.length;
var out = new Array(len);
var i = 0;
while (i < len) {
out[i] = fun(this[i], i, this);
i++;
}
return out;
}
Array.prototype.sMap = function(callback, thisArg) {
var _this, out, i;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
_this = thisArg;
}
out = new Array(len);
i = 0;
while (i < len) {
if (i in O) {
out[i] = callback.call(_this, O[i], i, O);
}
i++;
}
return out;
};
Array.prototype.ncMap = function(callback, thisArg) {
var _this, out, i;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
_this = thisArg;
}
out = new Array(len);
i = 0;
while (i < len) {
out[i] = callback.call(_this, O[i], i, O);
i++;
}
return out;
};
Array.prototype.cinncMap = function(callback, thisArg) {
var _this, out, i;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
out = new Array(len);
i = 0;
if (thisArg) {
_this = thisArg;
while (i < len) {
out[i] = callback.call(_this, O[i], i, O);
i++;
}
} else {
while (i < len) {
out[i] = callback(O[i], i, O);
i++;
}
}
return out;
};
Array.prototype.cinMap = function(callback, thisArg) {
var _this, out, i;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
out = new Array(len);
i = 0;
var kValue, mappedValue;
if (thisArg) {
_this = thisArg;
while (i < len) {
if (i in O) {
out[i] = callback.call(_this, O[i], i, O);
}
i++;
}
} else {
while (i < len) {
if (i in O) {
out[i] = callback(O[i], i, O);
}
i++;
}
}
return out;
};
Array.prototype.ncObjLitMap = function(callback, thisArg) {
var _this, out, i;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
_this = thisArg;
} else {
_this = {};
};
_this.callback = callback;
out = new Array(len);
i = 0;
while (i < len) {
out[i] = _this.callback(O[i], i, O);
i++;
}
delete _this.callback;
return out;
};
Array.prototype.bMap = function(cb, thisArg) {
var _this, out, i, callback;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof cb !== "function") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
_this = thisArg;
}
callback = cb.bind(_this);
out = new Array(len);
i = 0;
while (i < len) {
out[i] = callback(O[i], i, O);
i++;
}
return out;
};
Array.prototype.idMap = function(callback, thisArg) {
var _this, out, i;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
_this = thisArg;
}
out = new Array(len);
i = 0;
while (i < len) {
if (typeof O[i] !== "undefined") {
out[i] = callback.call(_this, O[i], i, O);
}
i++;
}
return out;
};
Array.prototype.okMap = function(callback, thisArg) {
var _this, out, i, keys;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var keys = Object.keys(O);
var len = O.keys >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
out = new Array(len);
i = 0;
if (thisArg) {
_this = thisArg;
while (i < len) {
out[i] = callback.call(_this, O[keys[i]], keys[i], O);
i++;
}
} else {
while (i < len) {
out[i] = callback(O[keys[i]], keys[i], O);
i++;
}
}
return out;
};
Array.prototype.ocisMap = function(callback, thisArg) {
var _this, out, i;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
_this = thisArg;
} else {
_this = {};
};
_this.callback = callback;
out = new Array(len);
i = 0;
if (len === Object.keys(O).length) {
while (i < len) {
out[i] = _this.callback(O[i], i, O);
i++;
}
} else {
while (i < len) {
if (i in O) {
out[i] = _this.callback(O[i], i, O);
}
i++;
}
}
delete _this.callback;
return out;
};
Ready to run.
Test | Ops/sec | |
---|---|---|
map |
| ready |
Quick Map |
| ready |
Specification Map |
| ready |
call only if needed |
| ready |
no check |
| ready |
no check+call only if needed |
| ready |
No Check + Obj Literal This map |
| ready |
Bind Map |
| ready |
If definded |
| ready |
use object keys to get subset |
| ready |
Only Check if Sparse |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.