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="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://unpkg.com/immer@1.7.2/dist/immer.umd.js"></script>
var objA = {"a":0.282,"z":{"a":{"a":"string 0.485","z":"string 0.926","c":"2014-12-17T16:35:40.641Z","d":4864,"e":"string 0.476","f":"string 0.881","g":1424,"h":"2006-11-24T13:56:33.666Z","i":0.486,"j":0.77},"z":{"a":829,"z":0.725}},"c":{"a":{"a":0.0621,"z":"string 0.512","c":0.154,"d":"1978-06-21T17:39:46.527Z","e":0.507,"f":null,"g":"1984-02-04T18:08:54.747Z","h":6444,"i":5151,"j":0.911,"k":"string 0.396","l":"string 0.345","m":8456,"b":0.814,"o":0.806,"p":0.33,"q":"1991-07-02T03:27:55.140Z","r":"1972-01-31T15:41:06.854Z","s":"1975-03-24T22:09:41.932Z","t":"string 0.774","u":"2013-08-20T08:41:23.297Z","v":7374,"w":null,"x":null,"y":0.93},"z":"string 0.180","c":"1973-01-26T19:55:14.208Z","d":"string 0.940","e":0.734,"f":0.569,"g":{"a":0.565,"z":0.114,"c":6911,"d":"string 0.0894","e":0.9,"f":8149,"g":"string 0.777","h":5352,"i":0.0555,"j":3497,"k":8192,"l":7546,"m":"1972-03-29T21:43:52.317Z","b":"1980-08-17T06:41:23.247Z","o":4047},"h":{"a":"2003-09-03T11:19:09.234Z","z":7333,"c":0.686,"d":2299,"e":2725,"f":9354,"g":0.249,"h":"string 0.385","i":3316,"j":null,"k":"2003-03-19T06:40:54.047Z","l":4642,"m":3240,"b":"2005-08-29T01:41:37.533Z","o":2718,"p":null,"q":"string 0.666","r":0.742,"s":"string 0.806","t":3896,"u":"1976-10-08T20:09:01.796Z","v":0.271,"w":5055,"x":2309,"y":null},"i":0.98,"j":{"a":0.226,"z":0.000491,"c":null},"k":"string 0.405","l":1816,"m":"1994-03-01T22:00:50.119Z"},"d":{"a":null,"z":"1985-09-06T03:06:54.808Z","c":64,"d":{"a":0.531,"z":0.377,"c":4587,"d":0.856,"e":0.539,"f":null,"g":"1984-07-06T19:32:18.650Z"},"e":"2009-04-27T14:54:04.938Z","f":"1999-07-29T04:24:42.978Z","g":7506,"h":{"a":"1982-05-22T04:38:05.190Z","z":8755,"c":"1979-06-02T19:59:20.129Z","d":0.0384,"e":8975,"f":2354,"g":4375,"h":8017,"i":5169,"j":0.465,"k":"string 0.663","l":0.467,"m":"string 0.0202","b":"string 0.436","o":7865,"p":9629,"q":2752,"r":null,"s":"1979-03-06T10:32:29.609Z","t":null},"i":2881,"j":"1970-09-20T18:03:24.404Z","k":"2010-05-31T10:09:26.766Z","l":"string 0.497","m":"1996-05-31T19:15:31.919Z","b":0.235,"o":"string 0.435","p":2774,"q":null,"r":{},"s":7788,"t":0.64},"e":{"a":"1974-08-17T04:54:55.936Z","z":9950,"c":0.834,"d":null,"e":1560,"f":{"a":5615,"z":"string 0.118","c":"2004-03-26T18:06:55.340Z","d":"string 0.226","e":4641,"f":0.313,"g":6474,"h":"2004-06-25T16:39:24.453Z","i":"2001-09-01T11:59:12.854Z","j":"1981-11-20T15:15:28.548Z","k":6240,"l":9246,"m":"1980-09-07T06:33:27.022Z","b":"string 0.177","o":"2010-04-21T18:46:52.602Z","p":"string 0.146","q":0.582},"g":{"a":"1995-02-26T16:26:01.082Z","z":"string 0.631","c":null,"d":"string 0.604","e":0.187,"f":null,"g":"1997-03-26T13:58:36.099Z","h":4705,"i":"string 0.846","j":"2006-09-15T20:28:54.014Z","k":null,"l":0.189},"h":4345,"i":"2006-10-22T20:50:47.528Z","j":"1977-02-27T01:39:23.588Z","k":{"a":8883,"z":0.149,"c":0.985},"l":0.679,"m":0.454,"b":8707},"f":null,"g":"string 0.753","h":0.912,"i":{"a":{"a":"string 0.0138","z":"1989-05-11T05:19:21.223Z","c":null,"d":"string 0.219","e":0.905,"f":1020,"g":7579,"h":2220,"i":9706,"j":"string 0.0511","k":"2007-03-05T17:31:56.981Z","l":"string 0.137"},"z":{"a":null,"z":"1983-11-14T18:01:13.237Z","c":6719,"d":0.972},"c":2650,"d":"string 0.167","e":0.825,"f":{"a":6305,"z":7918,"c":3888,"d":"1991-11-20T21:38:07.258Z","e":8280,"f":8533,"g":null,"h":"string 0.906","i":null,"j":"2004-01-08T02:22:04.633Z","k":0.386,"l":"string 0.108","m":0.253,"b":"1974-06-22T10:52:07.627Z","o":0.353,"p":6243,"q":6584},"g":0.542,"h":8161,"i":"2010-10-05T19:33:07.930Z","j":{"a":8592,"z":"string 0.699","c":6818},"k":0.359,"l":"2000-07-27T07:52:37.702Z","m":{"a":"2005-08-09T18:35:32.950Z","z":0.0427,"c":4048,"d":0.525,"e":0.319,"f":"string 0.917","g":"string 0.990","h":446,"i":null,"j":null},"b":{"a":5924,"z":0.00489,"c":"2014-05-12T01:40:46.595Z","d":"1987-10-12T06:50:26.836Z","e":"string 0.455","f":3730,"g":8717,"h":null,"i":"2010-02-24T03:47:10.056Z","j":"string 0.720","k":"string 0.112","l":211},"o":"2000-09-03T13:19:43.114Z","p":"1989-12-03T09:32:10.660Z"},"j":3672,"k":8634};
var objB = {"a":0.282,"z":{"a":{"a":"string 0.485","z":"string 0.926","c":"2014-12-17T16:35:40.641Z","d":4864,"e":"string 0.476","f":"string 0.881","g":1424,"h":"2006-11-24T13:56:33.666Z","i":0.486,"j":0.77},"z":{"a":829,"z":0.725}},"c":{"a":{"a":0.0621,"z":"string 0.512","c":0.154,"d":"1978-06-21T17:39:46.527Z","e":0.507,"f":null,"g":"1984-02-04T18:08:54.747Z","h":6444,"i":5151,"j":0.911,"k":"string 0.396","l":"string 0.345","m":8456,"b":0.814,"o":0.806,"p":0.33,"q":"1991-07-02T03:27:55.140Z","r":"1972-01-31T15:41:06.854Z","s":"1975-03-24T22:09:41.932Z","t":"string 0.774","u":"2013-08-20T08:41:23.297Z","v":7374,"w":null,"x":null,"y":0.93},"z":"string 0.180","c":"1973-01-26T19:55:14.208Z","d":"string 0.940","e":0.734,"f":0.569,"g":{"a":0.565,"z":0.114,"c":6911,"d":"string 0.0894","e":0.9,"f":8149,"g":"string 0.777","h":5352,"i":0.0555,"j":3497,"k":8192,"l":7546,"m":"1972-03-29T21:43:52.317Z","b":"1980-08-17T06:41:23.247Z","o":4047},"h":{"a":"2003-09-03T11:19:09.234Z","z":7333,"c":0.686,"d":2299,"e":2725,"f":9354,"g":0.249,"h":"string 0.385","i":3316,"j":null,"k":"2003-03-19T06:40:54.047Z","l":4642,"m":3240,"b":"2005-08-29T01:41:37.533Z","o":2718,"p":null,"q":"string 0.666","r":0.742,"s":"string 0.806","t":3896,"u":"1976-10-08T20:09:01.796Z","v":0.271,"w":5055,"x":2309,"y":null},"i":0.98,"j":{"a":0.226,"z":0.000491,"c":null},"k":"string 0.405","l":1816,"m":"1994-03-01T22:00:50.119Z"},"d":{"a":null,"z":"1985-09-06T03:06:54.808Z","c":64,"d":{"a":0.531,"z":0.377,"c":4587,"d":0.856,"e":0.539,"f":null,"g":"1984-07-06T19:32:18.650Z"},"e":"2009-04-27T14:54:04.938Z","f":"1999-07-29T04:24:42.978Z","g":7506,"h":{"a":"1982-05-22T04:38:05.190Z","z":8755,"c":"1979-06-02T19:59:20.129Z","d":0.0384,"e":8975,"f":2354,"g":4375,"h":8017,"i":5169,"j":0.465,"k":"string 0.663","l":0.467,"m":"string 0.0202","b":"string 0.436","o":7865,"p":9629,"q":2752,"r":null,"s":"1979-03-06T10:32:29.609Z","t":null},"i":2881,"j":"1970-09-20T18:03:24.404Z","k":"2010-05-31T10:09:26.766Z","l":"string 0.497","m":"1996-05-31T19:15:31.919Z","b":0.235,"o":"string 0.435","p":2774,"q":null,"r":{},"s":7788,"t":0.64},"e":{"a":"1974-08-17T04:54:55.936Z","z":9950,"c":0.834,"d":null,"e":1560,"f":{"a":5615,"z":"string 0.118","c":"2004-03-26T18:06:55.340Z","d":"string 0.226","e":4641,"f":0.313,"g":6474,"h":"2004-06-25T16:39:24.453Z","i":"2001-09-01T11:59:12.854Z","j":"1981-11-20T15:15:28.548Z","k":6240,"l":9246,"m":"1980-09-07T06:33:27.022Z","b":"string 0.177","o":"2010-04-21T18:46:52.602Z","p":"string 0.146","q":0.582},"g":{"a":"1995-02-26T16:26:01.082Z","z":"string 0.631","c":null,"d":"string 0.604","e":0.187,"f":null,"g":"1997-03-26T13:58:36.099Z","h":4705,"i":"string 0.846","j":"2006-09-15T20:28:54.014Z","k":null,"l":0.189},"h":4345,"i":"2006-10-22T20:50:47.528Z","j":"1977-02-27T01:39:23.588Z","k":{"a":8883,"z":0.149,"c":0.985},"l":0.679,"m":0.454,"b":8707},"f":null,"g":"string 0.753","h":0.912,"i":{"a":{"a":"string 0.0138","z":"1989-05-11T05:19:21.223Z","c":null,"d":"string 0.219","e":0.905,"f":1020,"g":7579,"h":2220,"i":9706,"j":"string 0.0511","k":"2007-03-05T17:31:56.981Z","l":"string 0.137"},"z":{"a":null,"z":"1983-11-14T18:01:13.237Z","c":6719,"d":0.972},"c":2650,"d":"string 0.167","e":0.825,"f":{"a":6305,"z":7918,"c":3888,"d":"1991-11-20T21:38:07.258Z","e":8280,"f":8533,"g":null,"h":"string 0.906","i":null,"j":"2004-01-08T02:22:04.633Z","k":0.386,"l":"string 0.108","m":0.253,"b":"1974-06-22T10:52:07.627Z","o":0.353,"p":6243,"q":6584},"g":0.542,"h":8161,"i":"2010-10-05T19:33:07.930Z","j":{"a":8592,"z":"string 0.699","c":6818},"k":0.359,"l":"2000-07-27T07:52:37.702Z","m":{"a":"2005-08-09T18:35:32.950Z","z":0.0427,"c":4048,"d":0.525,"e":0.319,"f":"string 0.917","g":"string 0.990","h":446,"i":null,"j":null},"b":{"a":5924,"z":0.00489,"c":"2014-05-12T01:40:46.595Z","d":"1987-10-12T06:50:26.836Z","e":"string 0.455","f":3730,"g":8717,"h":null,"i":"2010-02-24T03:47:10.056Z","j":"string 0.720","k":"string 0.112","l":211},"o":"2000-09-03T13:19:43.114Z","p":"1989-12-03T09:32:10.660Z"},"j":3672,"k":8634};
var objC = immer.produce(objA, function(draft) {
draft.a = 2;
});
Object.equals = function( x, y ) {
if ( x === y ) return true;
// if both x and y are null or undefined and exactly the same
if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) return false;
// if they are not strictly equal, they both need to be Objects
if ( x.constructor !== y.constructor ) return false;
// they must have the exact same prototype chain, the closest we can do is
// test there constructor.
for ( var p in x ) {
if ( ! x.hasOwnProperty( p ) ) continue;
// other properties were tested using x.constructor === y.constructor
if ( ! y.hasOwnProperty( p ) ) return false;
// allows to compare x[ p ] and y[ p ] when set to undefined
if ( x[ p ] === y[ p ] ) continue;
// if they have the same strict value or identity then they are equal
if ( typeof( x[ p ] ) !== "object" ) return false;
// Numbers, Strings, Functions, Booleans must be strictly equal
if ( ! Object.equals( x[ p ], y[ p ] ) ) return false;
// Objects and Arrays must be tested recursively
}
for ( p in y ) {
if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) ) return false;
// allows x[ p ] to be set to undefined
}
return true;
}
const hasOwn = Object.prototype.hasOwnProperty
function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
}
function shallowEqual(objA, objB) {
if (is(objA, objB)) return true
if (typeof objA !== 'object' || objA === null ||
typeof objB !== 'object' || objB === null) {
return false
}
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
if (keysA.length !== keysB.length) return false
for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
return true
}
const TS = Object.prototype.toString;
const OA = '[object Array]';
const OO = '[object Object]';
function flatDeepDiff(prev, next) {
if (TS.call(prev) !== OO || TS.call(next) !== OO) {
throw new TypeError('parameter must be object');
}
const flatDiff = {};
const initPath = '';
// fillKeys(prev, next);
deepDiff(prev, next, initPath, flatDiff);
if (Object.keys(flatDiff).length === 0) {
return null;
}
return flatDiff;
}
/**
* 把 prevData 中存在但 nextData 中不存在的 key 赋值 null, 包括数组中的对象
*
* { a: 1, b: 2 }, { a: 1 } => { a: 1, b: 2 }, { a: 1, b: null }
* [{ a: 1, b: 2 }], [{ a: 1 }] => [{ a: 1, b: 2 }], [{ a: 1, b: null }]
*
* nextData 的数组长度小于 prevData 的数组长度的时候,不做数组 deepDiff,这种情况尽量采用 $spliceData
* 删除数组中一项:
commit(type, payload, {
meta: {
splicePath: 'path', // eg. 'todos'
spliceData: [start, deleteCount, ...items] // eg. [100, 1]
}
});
*/
function fillKeys(prev, next) {
if (prev === next) return;
const ntype = TS.call(next);
const ptype = TS.call(prev);
const isOO = ntype === OO && ptype === OO;
const isOA = ntype === OA && ptype === OA;
if (isOO) {
for (let key in prev) {
if (!prev.hasOwnProperty(key)) return;
if (next[key] === undefined) {
next[key] = null;
} else {
fillKeys(prev[key], next[key]);
}
}
} else if (isOA && next.length >= prev.length) {
prev.forEach((item, index) => {
fillKeys(next[index], item);
});
}
}
function deepDiff(prev, next, path, flatDiff) {
if (prev === next) return;
const ntype = TS.call(next);
const ptype = TS.call(prev);
const isOO = ntype === OO && ptype === OO;
const isOA = ntype === OA && ptype === OA;
if (isOO) {
for (var key in next) {
if (!next.hasOwnProperty(key)) return;
const flatKey = path + (path.length ? '.' + key : key);
const nValue = next[key];
const pValue = prev[key];
const nVtype = TS.call(nValue);
const pVtype = TS.call(pValue);
if (nVtype !== OO && nVtype !== OA) {
if (nValue !== pValue) {
flatDiff[flatKey] = nValue;
}
} else if (nVtype === OA) {
if (pVtype !== OA || nValue.length < pValue.length) {
flatDiff[flatKey] = nValue;
} else {
deepDiff(pValue, nValue, flatKey, flatDiff);
// nValue.forEach((item, idx) => {
// deepDiff(pValue[idx], item, flatKey + '[' + idx + ']', flatDiff);
// });
}
} else if (nVtype === OO) {
if (pVtype !== OO) {
flatDiff[flatKey] = nValue;
} else {
deepDiff(pValue, nValue, flatKey, flatDiff);
// for (var sk in nValue) {
// deepDiff(pValue[sk], nValue[sk], flatKey + '.' + sk, flatDiff);
// }
}
}
}
} else if (isOA && next.length >= prev.length) {
next.forEach((item, idx) => {
deepDiff(prev[idx], item, path + '[' + idx + ']', flatDiff);
});
} else {
flatDiff[path] = next;
}
return flatDiff;
}
const ARRAYTYPE = '[object Array]'
const OBJECTTYPE = '[object Object]'
const FUNCTIONTYPE = '[object Function]'
function westoreDiff(current, pre) {
const result = {}
syncKeys(current, pre)
_diff(current, pre, '', result)
return result
}
function syncKeys(current, pre) {
if (current === pre) return
const rootCurrentType = type(current)
const rootPreType = type(pre)
if (rootCurrentType == OBJECTTYPE && rootPreType == OBJECTTYPE) {
if(Object.keys(current).length >= Object.keys(pre).length){
for (let key in pre) {
const currentValue = current[key]
if (currentValue === undefined) {
current[key] = null
} else {
syncKeys(currentValue, pre[key])
}
}
}
} else if (rootCurrentType == ARRAYTYPE && rootPreType == ARRAYTYPE) {
if (current.length >= pre.length) {
pre.forEach((item, index) => {
syncKeys(current[index], item)
})
}
}
}
function _diff(current, pre, path, result) {
if (current === pre) return
const rootCurrentType = type(current)
const rootPreType = type(pre)
if (rootCurrentType == OBJECTTYPE) {
if (rootPreType != OBJECTTYPE || Object.keys(current).length < Object.keys(pre).length) {
setResult(result, path, current)
} else {
for (let key in current) {
const currentValue = current[key]
const preValue = pre[key]
const currentType = type(currentValue)
const preType = type(preValue)
if (currentType != ARRAYTYPE && currentType != OBJECTTYPE) {
if (currentValue != pre[key]) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
}
} else if (currentType == ARRAYTYPE) {
if (preType != ARRAYTYPE) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
} else {
if (currentValue.length < preValue.length) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
} else {
currentValue.forEach((item, index) => {
_diff(item, preValue[index], (path == '' ? '' : path + ".") + key + '[' + index + ']', result)
})
}
}
} else if (currentType == OBJECTTYPE) {
if (preType != OBJECTTYPE || Object.keys(currentValue).length < Object.keys(preValue).length) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
} else {
for (let subKey in currentValue) {
_diff(currentValue[subKey], preValue[subKey], (path == '' ? '' : path + ".") + key + '.' + subKey, result)
}
}
}
}
}
} else if (rootCurrentType == ARRAYTYPE) {
if (rootPreType != ARRAYTYPE) {
setResult(result, path, current)
} else {
if (current.length < pre.length) {
setResult(result, path, current)
} else {
current.forEach((item, index) => {
_diff(item, pre[index], path + '[' + index + ']', result)
})
}
}
} else {
setResult(result, path, current)
}
}
function setResult(result, k, v) {
if (type(v) != FUNCTIONTYPE) {
result[k] = v
}
}
function type(obj) {
return Object.prototype.toString.call(obj)
}
Ready to run.
Test | Ops/sec | |
---|---|---|
flatDeepDiff |
| ready |
JSON.stringify |
| ready |
Object.equals |
| ready |
_.isEqual |
| ready |
shallowEqual |
| ready |
flatDeepDiff-immer |
| ready |
westoreDiff |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.