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="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script>
/*
* belt.js
*
* A general-purpose utility function. Used to extend
* and house my general-purpose js-utils.
*
*/
// Testing shim.
// Node or DOM.
var global = global || window;
(function(global) {
/*
* SETUP
*
* Initialize public and private variables, and overwrite
* the `global.BELT` namespace, caching anything that might be there.
* Orient document, navigator, location to the global object, since we're
* operating inside a self-invoking function closure.
*
* BELT : Global namespace for utility functions.
*
* _cache : A private cache object accessed by BELT fns.
* _errors : A private error queue; populated by BELT error logging.
* _belt : A copy of anything in the `global.BELT` namespace, in case
* we end up overwriting something.
* _console : A copy of the `global.console` if it exists. BELT defaults to
* creating a safe copy of console in the global namespace,
* overwriting the normal `global.console`.
*
*/
var BELT = {},
_cache = {},
_errors = [],
// Make sure document, location, navigator are properly defined
// within the scope of this self-invoking function.
document = global.document,
location = global.location,
navigator = global.navigator;
// Since we'll be overwriting global.BELT and global.console
// make the old values accessible via the _cache.
_cache.global_BELT = global.BELT;
_cache.global_console = global.console;
/* ---------------- top ----------------- */
/*
* ECMA Shims
*
* Date.now()
* Not available in LT ECMA1.5
* Creates version of Date.now if not supported.
* Perf testing: `new Date().getTime()` > `+(new Date)` by a lot.
* http://jsperf.com/creating-timestamps
*
* Array.indexOf()
* This method is much more efficient at telling whether an object
* exists in an array--especially if it's a complex object.
* Method not supported/partially supported in >= IE9, and is worth
* shimming.
* Uses Mozilla ECMA-262 shim, see:
* https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf
*
*/
Date.now = Date.now || function now() { return new Date().getTime() };
// Precompressed to save space.
Array.prototype.indexOf = Array.prototype.indexOf ||
function indexOf(d){
var c,b,a;if(void 0===this||null===this)throw new TypeError;
c=Object(this);b=c.length>>>0;if(0===b)return-1;a=0;
0<arguments.length&&(a=Number(arguments[1]),a!==a?a=0:0!==a&&
(a!==1/0&&a!==-(1/0))&&(a=(0<a||-1)*Math.floor(Math.abs(a))));
if(a>=b)return-1;for(a=0<=a?a:Math.max(b-Math.abs(a),0);a<b;a++)
{if(a in c&&c[a]===d)return a;}return -1;
};
/* ------------- ecma shim -------------- */
/*
* CORE JS UTILITIES
*
* Borrow various native functions for internal use.
* Any self-rolled object and array manipulation functions go here.
*
* slice()
* Array.prototype.slice
*
* push()
* Array.prototype.push
*
* toString()
* Object.prototype.toString
*
* hasOwnProperty()
* Object.prototype.hasOwnProperty
*
*
* typeof x functions
* Using type-check functions is a hell of a lot more efficient than
* the string parsing that Underscore does (at least as of 1.4.3).
* http://jsperf.com/is-it-a-number
* Object, Function, String, Number
* @argument : thing
* @return : [boolean]
*
* instanceof functions
* Checking things that can't be checked with typeof.
* Array, Element, Date, Regex, Error
* @argument : thing
* @return : [boolean]
*
* areArraysEqual()
* Checks if the arrays are equal. Because this is where JS sucks.
* @arguments : Array A, Array B
* @return : boolean
*
* isEmpty()
* Checks whether a thing is "empty".
* Checking if Functions are empty is somewhat strange, has 3 criteria:
* 1. no name,
* 2. no arguments,
* 3. no operations in curly braces.
* Roughly a port of Underscore's _.isEmpty() fn.
* h/t Jeremy Ashkenas. http://underscorejs.org
* @arguments : something
* @return : [boolean]
*
* clone()
* Create a deep copy of an object via recursion.
* This is more effective than JSON.* means of cloning objects,
* since cloned functions are usable.
* This is for non-prototypal object literals and arrays, rather
* than constructed objects... for the moment.
* Currently outstrips jQuery.extend() [v 1.8.3] in performance
* by a significant margin : http://jsperf.com/belt-core-deep-clone
* @arguments : from [,useHard]
* @return : [clone of object]
*
*
* extend()
* @dep : core.clone(), core.areArraysEqual()
* @arguments : target, object [,useHard]
* @return : boolean
*
*
* namespace()
* Return the tip of a namespace. If it doesn't exist yet, then
* create an object in this namespace.
* Using `BELT.namespace()` prevents accidental rewrites when
* working with big namespaces.
* @arguments : [string]
* @return : [Object object]
*
* inherit()
* A temporary-constructor inheritance pattern.
* @arguments : parent, child.
* {parent:, child:}
* @return : ...
*
*
* createConsoleAlias()
* The JavaScript global console object is not consistently implemented
* cross brwoser yet.
* This creates a `console` object to prevent script crashes when you
* attempt to access a method of an undefined object. Methods are aliased
* in stages based on browser availability.
* As far as I know, I've grouped the commands by availability in different
* browser environments. The most generic set, `ie_opera_ff_webkit` will
* work pretty reliably x-browser when you use it.
* @arguments : none
* @return : Pseudo-console.
*
*/
BELT.core = {};
BELT.slice = BELT.core.slice = Array.prototype.slice;
BELT.push = BELT.core.push = Array.prototype.push;
BELT.toString = BELT.core.toString = Object.prototype.toString;
BELT.hasOwnProperty = BELT.core.hasOwnProperty = Object.prototype.hasOwnProperty;
BELT.isObject = BELT.core.isObject = function(it) {
return typeof it === 'object';
};
BELT.isFunction = BELT.core.isFunction = function(it) {
return typeof it === 'function';
};
BELT.isString = BELT.core.isString = function(it) {
return typeof it === 'string';
};
BELT.isNumber = BELT.core.isNumber = function(it) {
return typeof it === 'number';
};
BELT.isBoolean = BELT.core.isBoolean = function(it) {
return typeof it === 'boolean';
};
BELT.isUndefined = BELT.core.isUndefined = function(it) {
return typeof it === 'undefined';
};
BELT.isArray = BELT.core.isArray = function(it) {
return it instanceof Array;
};
BELT.isElement = BELT.core.isElement = function(it) {
return it instanceof Element;
};
BELT.isDate = BELT.core.isDate = function(it) {
return it instanceof Date;
};
BELT.isRegex = BELT.core.isRegex = function(it) {
return it instanceof Regex;
};
BELT.isError = BELT.core.isError = function(it) {
return it instanceof Error;
};
BELT.areArraysEqual = BELT.core.areArraysEqual = function(a, b) {
return !(a<b || b>a);
};
BELT.isEmpty = BELT.core.isEmpty = function(it) {
var prop;
if (it === undefined) {
return true;
} else if (BELT.core.isNumber(it)) {
return false;
} else if (BELT.core.isArray(it) || BELT.core.isString(it)) {
return it.length === 0;
} else if (BELT.core.isFunction(it)) {
return it.name === '' && (/\(\s*\)\s*{\s*}/).test(a);
} else if (BELT.core.isObject(it)) {
for (prop in it) {
if (it.hasOwnProperty(prop)) { return false; }
}
}
return true;
};
BELT.isEqual_d1 = BELT.core.isEqual_d1 = (function(){
function recurse(a_i, b_i) {
//console.log('recursing...');
//console.log('a_i : ');
//console.log(a_i);
//console.log('b_i : ');
//console.log(b_i);
if (BELT.core.isObject(a_i)) {
//console.log('array or object at this unit. go deep.');
return BELT.core.isEqual_d1(a_i, b_i);
} else {
//console.log('primitives. equate.');
return a_i === b_i;
}
};
return function isEqual_d1(a, b) {
var type_a,
type_b,
constructor_a,
constructor_b,
prop,
i,
k;
type_a = typeof a;
type_b = typeof b;
constructor_a = a.constructor;
constructor_b = b.constructor;
// TYPECHECK
// Do a/b exist?
// This we can't check for, since we might be testing null vs
// undefined.
// BUT, we can check types to make sure this is worth processing
// at all. If they're the wrong types altogether, who cares?
// This will encapsulate the null/undefined test case.
//
// Check primitives (typeof)
if (type_a !== type_b) { return false; }
//console.log('Pass: primitives.');
// Check advanced types, using constructors.
if (constructor_a !== constructor_b) { return false; }
//console.log('Pass: constructors.');
// !!! WARNING - DO NOT UNDERSTAND DO NOT WANT
//
// 0 , -0 IDENTITY CASE
// "Equal" in JavaScript, but not the same.
if (a === b) { return a !== 0 || 1/a == 1/b; }
//console.log('Pass: identity case.');
// EMPTY
// If they're empty, do all that before we start manipulating
// strings and other pricey procedures.
if (BELT.isEmpty(a) && BELT.isEmpty(b)) { return true; }
//console.log('Pass: empty array/object.');
// PRIMITIVE EQUIVALENCES
// Comparison switch for comparable primitive types.
if (type_a === 'string' || type_a === 'boolean' || type_a === 'number') {
return a === b;
}
//console.log('Pass: primitive eq.');
// COMPLEX TYPE EQUIVALENCE
// Comparison for comparable complex types for things that
// aren't objects.
// Dates
// Convert into comparable units, AKA milliseconds, to judge.
// RegExp
// Deconstruct component parts to see if they are eq all through.
// h/t Jeremy Ashkenas, Underscore.
// Function
// Similar to isEmpty(), we compare two things:
// - name
// - stringified function contents
// a = function Panda(){}
// b = function Fox(){}
// a eq b
// => false
//
// a = function( ){ }
// b = function (){ }
// a eq b
// => true
//
switch (constructor_a) {
case Date:
return a.getTime() == b.getTime();
case RegExp:
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
case Function:
return a.name === b.name &&
(a.toString() === b.toString() ||
BELT.isEmpty(a) && BELT.isEmpty(b));
}
//console.log('Pass: simple (comparative) complex-type equivalence.');
// LENGTH EVALUATION
// Does this array have the same number of elements? or, does
// this object have the same number of properties?
if (constructor_a === Array && a.length !== b.length) {
return false;
}
//console.log('Pass: simple length complex-type equivalence.');
// RECURSAL EVALUATION
// If we end up with two things that aren't equal,
// break out as false as soon as you do.
if (constructor_a === Array) {
//console.log('Recursal eval: Array');
for (i = 0; i < a.length; i += 1) {
if (recurse(a[i], b[i]) === false) { return false; }
}
} else if (constructor_a === Object) {
//console.log('Recursal eval: object');
for (i in a) {
if (recurse(a[i], b[i]) === false) { return false; }
}
}
// OK.
// We threw our best at it, and it didn't budge. It's probably equal.
return true;
};
}());
// Adapted from Underscore's isEqual
// h/t Jeremy Askenas
//
// What does it mean to be equal?
// Strictly equivalent.
//
// 1 eq "1" => false
// null eq undefined => false
// NOTE: Requires strict type checking, not just 'truthiness'
// 1 eq true => false
BELT.isEqual_d3 = BELT.core.isEqual_d3 = (function(){
function recurse(a_i, b_i) {
if (BELT.core.isObject(a_i)) {
return BELT.core.isEqual_d3(a_i, b_i);
} else {
return a_i === b_i;
}
};
return function isEqual_d3(a, b) {
var type_a,
type_b,
constructor_a,
constructor_b,
prop,
i;
type_a = typeof a;
type_b = typeof b;
constructor_a = a.constructor;
constructor_b = b.constructor;
// TYPECHECK
// Do a/b exist?
// This we can't check for, since we might be testing null vs
// undefined.
// BUT, we can check types to make sure this is worth processing
// at all. If they're the wrong types altogether, who cares?
// This will encapsulate the null/undefined test case.
//
// Check primitives (typeof)
if (type_a !== type_b) { return false; }
// Check advanced types, using constructors.
if (constructor_a !== constructor_b) { return false; }
// 0 , -0 IDENTITY CASE
// "Equal" in JavaScript, but not the same.
// h/t Jeremy Askenas, Underscore.
if (a === b) { return a !== 0 || 1/a == 1/b; }
// PRIMITIVE EQUIVALENCES
// Comparison switch for comparable primitive types.
if (type_a === 'string') {
return a === b;
} else if (type_a === 'boolean'){
return a === b;
} else if (type_a === 'number') {
return a === b;
}
// EMPTY
// If they're empty, do all that before we start manipulating
// strings and other pricey procedures.
if (BELT.core.isEmpty(a) && BELT.core.isEmpty(b)) { return true; }
// COMPLEX TYPE EQUIVALENCE
// Comparison for comparable complex types for things that
// aren't objects.
// Dates
// Convert into comparable units, AKA milliseconds, to judge.
// RegExp
// Deconstruct component parts to see if they are eq all through.
// h/t Jeremy Ashkenas, Underscore.
// Function
// Similar to isEmpty(), we compare two things:
// - name
// - stringified function contents
// a = function Panda(){}
// b = function Fox(){}
// a eq b
// => false
//
// a = function( ){ }
// b = function (){ }
// a eq b
// => true
//
if (constructor_a === Date) {
return a.getTime() == b.getTime();
} else if (constructor_a === RegExp) {
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
} else if (constructor_a === Function) {
return a.name === b.name &&
(a.toString() === b.toString() ||
BELT.isEmpty(a) && BELT.isEmpty(b));
}
// RECURSAL AND LENGTH EVALUATION
// If we end up with two things that aren't equal,
// break out as false as soon as you do.
//
// Does this array have the same number of elements? or, does
// this object have the same number of properties?
if (constructor_a === Array) {
len = a.length;
if (len !== b.length) { return false; }
for (i = 0; i < len; i += 1) {
if (recurse(a[i], b[i]) === false) { return false; }
}
} else if (constructor_a === Object) {
for (i in a) {
if (recurse(a[i], b[i]) === false) { return false; }
}
}
// OK.
// We threw our best at it, and it didn't budge. It's probably equal.
return true;
};
}());
// Adapted from Underscore's isEqual
// h/t Jeremy Askenas
//
// What does it mean to be equal?
// Strictly equivalent.
//
// 1 eq "1" => false
// null eq undefined => false
// NOTE: Requires strict type checking, not just 'truthiness'
// 1 eq true => false
BELT.isEqual_d4 = BELT.core.isEqual_d4 = (function(){
function recurse(a_i, b_i) {
if (BELT.core.isObject(a_i)) {
return BELT.core.isEqual_d4(a_i, b_i);
} else {
return a_i === b_i;
}
};
return function isEqual_d4(a, b) {
var type_a,
type_b,
constructor_a,
constructor_b,
prop,
i;
type_a = typeof a;
type_b = typeof b;
constructor_a = a.constructor;
constructor_b = b.constructor;
// TYPECHECK
// Do a/b exist?
// This we can't check for, since we might be testing null vs
// undefined.
// BUT, we can check types to make sure this is worth processing
// at all. If they're the wrong types altogether, who cares?
// This will encapsulate the null/undefined test case.
//
// Check primitives (typeof)
if (type_a !== type_b || constructor_a !== constructor_b) {
return false;
}
// 0 , -0 IDENTITY CASE
// "Equal" in JavaScript, but not the same.
// h/t Jeremy Askenas, Underscore.
if (a === b) { return a !== 0 || 1/a == 1/b; }
// PRIMITIVE EQUIVALENCES
// Comparison switch for comparable primitive types.
// We've already seen that a === b .. > false, otherwise,
// we would have gone into the if statement up there.
if (type_a === 'string' || type_a === 'boolean' || type_a === 'number') {
return false;
}
// COMPLEX TYPE EQUIVALENCE
// Comparison for comparable complex types for things that
// aren't objects.
// Dates
// Convert into comparable units, AKA milliseconds, to judge.
// RegExp
// Deconstruct component parts to see if they are eq all through.
// h/t Jeremy Ashkenas, Underscore.
// Function
// Similar to isEmpty(), we compare two things:
// - name
// - stringified function contents
// a = function Panda(){}
// b = function Fox(){}
// a eq b
// => false
//
// a = function( ){ }
// b = function (){ }
// a eq b
// => true
//
if (constructor_a === Date) {
return a.getTime() == b.getTime();
} else if (constructor_a === RegExp) {
return a.source == b.source &&
a.global == b.global &&
a.ignoreCase == b.ignoreCase &&
a.multiline == b.multiline;
} else if (constructor_a === Function) {
return a.name === b.name &&
(a.toString() === b.toString() ||
BELT.isEmpty(a) && BELT.isEmpty(b));
// continues below...
// RECURSAL AND LENGTH EVALUATION
// If we end up with two things that aren't equal,
// break out as false as soon as you do.
//
// Does this array have the same number of elements? or, does
// this object have the same number of properties?
} else if (constructor_a === Array) {
if (a.length !== b.length) { return false; }
len = a.length;
for (i = 0; i < len; i += 1) {
if (recurse(a[i], b[i]) === false) { return false; }
}
} else if (constructor_a === Object) {
for (i in a) {
if (!b[i]) { return false; } //Test keys first.
if (recurse(a[i], b[i]) === false) { return false; }
}
}
// OK.
// We threw our best at it, and it didn't budge. It's probably equal.
return true;
};
}());
BELT.isEqual_d6 = BELT.core.isEqual_d6 = (function(){
function recurse(a_i, b_i) {
if (BELT.core.isObject(a_i)) {
return BELT.core.isEqual_d6(a_i, b_i);
} else {
return a_i === b_i;
}
};
return function isEqual_d6(a, b) {
var type_a,
type_b,
constructor_a,
constructor_b,
len_a,
prop,
i;
// 0 , -0 IDENTITY CASE
// "Equal" in JavaScript, but not the same.
// h/t Jeremy Askenas, Underscore.
if (a === b) { return a !== 0 || 1/a == 1/b; }
// TYPECHECK
// Do a/b exist?
// This we can't check for, since we might be testing null vs
// undefined.
// BUT, we can check types to make sure this is worth processing
// at all. If they're the wrong types altogether, who cares?
// This will encapsulate the null/undefined test case.
//
// Check primitives (typeof)
type_a = typeof a;
type_b = typeof b;
constructor_a = a.constructor;
constructor_b = b.constructor;
if (type_a !== type_b) { return false; }
if (constructor_a !== constructor_b) { return false; }
// PRIMITIVE EQUIVALENCES
// Comparison switch for comparable primitive types.
if (type_a === 'string') {
return a === b;
} else if (type_a === 'boolean'){
return a === b;
} else if (type_a === 'number') {
return a === b;
}
// EMPTY
// If they're empty, do all that before we start manipulating
// strings and other pricey procedures.
// if (BELT.core.isEmpty(a) && BELT.core.isEmpty(b)) { return true; }
// COMPLEX TYPE EQUIVALENCE
// Comparison for comparable complex types for things that
// aren't objects.
// Dates
// Convert into comparable units, AKA milliseconds, to judge.
// RegExp
// Deconstruct component parts to see if they are eq all through.
// h/t Jeremy Ashkenas, Underscore.
// Function
// Similar to isEmpty(), we compare two things:
// - name
// - stringified function contents
// a = function Panda(){}
// b = function Fox(){}
// a eq b
// => false
//
// a = function( ){ }
// b = function (){ }
// a eq b
// => true
//
if (constructor_a === Date) {
return a.getTime() == b.getTime();
} else if (constructor_a === RegExp) {
return a.source == b.source &&
a.global == b.global &&
a.ignoreCase == b.ignoreCase &&
a.multiline == b.multiline;
} else if (constructor_a === Function) {
return a.name === b.name &&
(a.toString() === b.toString() ||
BELT.isEmpty(a) && BELT.isEmpty(b));
// continues below...
// RECURSAL AND LENGTH EVALUATION
// If we end up with two things that aren't equal,
// break out as false as soon as you do.
//
// Does this array have the same number of elements? or, does
// this object have the same number of properties?
} else if (constructor_a === Array) {
len_a = a.length;
if (len_a !== b.length) { return false; }
for (i = 0; i < len_a; i += 1) {
if (recurse(a[i], b[i]) === false) { return false; }
}
} else if (constructor_a === Object) {
for (i in a) {
if (!b[i]) { return false; }
if (recurse(a[i], b[i]) === false) { return false; }
}
}
// OK.
// We threw our best at it, and it didn't budge. It's probably equal.
return true;
};
}());
// D7 Draft
//
// Observations:
// - Constructor access is really slow.
// - Use primitive/complex type filtering early on
// (sort boolean, number, undefined, etc. and process
// differently from objects)
// - Don't run constructor tests; run instanceof on any complex obj.
BELT.isEqual_d7 = BELT.core.isEqual_d7 = (function(){
function recurse(a_i, b_i) {
if (BELT.core.isObject(a_i)) {
return BELT.core.isEqual_d7(a_i, b_i);
} else {
return a_i === b_i;
}
};
return function isEqual_d7(a, b) {
var type_a,
type_b,
constructor_a,
constructor_b,
len_a,
prop,
i;
// TYPECHECK
// Do a/b exist?
// This we can't check for, since we might be testing null vs
// undefined.
// BUT, we can check types to make sure this is worth processing
// at all. If they're the wrong types altogether, who cares?
// This will encapsulate the null/undefined test case.
//
// Check primitives (typeof)
type_a = typeof a;
type_b = typeof b;
if (type_a !== type_b) { return false; }
// PRIMITIVES AND MEMORY EQUIVALENCE
// "Equal" in JavaScript -- pointing to same space in memory,
// or two primitives.
// h/t Jeremy Askenas, Underscore.
if (a === b) { return a !== 0 || 1/a == 1/b; }
// COMPLEX OBJECTS
constructor_a = a.constructor;
constructor_b = b.constructor;
if (constructor_a !== constructor_b) { return false; }
/* -------------- */
// COMPLEX TYPE EQUIVALENCE
// Comparison for comparable complex types for things that
// aren't objects.
// Dates
// Convert into comparable units, AKA milliseconds, to judge.
// RegExp
// Deconstruct component parts to see if they are eq all through.
// h/t Jeremy Ashkenas, Underscore.
// Function
// Similar to isEmpty(), we compare two things:
// - name
// - stringified function contents
// a = function Panda(){}
// b = function Fox(){}
// a eq b
// => false
//
// a = function( ){ }
// b = function (){ }
// a eq b
// => true
//
if (constructor_a === Date) {
return a.getTime() == b.getTime();
} else if (constructor_a === RegExp) {
return a.source == b.source &&
a.global == b.global &&
a.ignoreCase == b.ignoreCase &&
a.multiline == b.multiline;
} else if (constructor_a === Function) {
return a.name === b.name &&
(a.toString() === b.toString() ||
BELT.isEmpty(a) && BELT.isEmpty(b));
// continues below...
// RECURSAL AND LENGTH EVALUATION
// If we end up with two things that aren't equal,
// break out as false as soon as you do.
//
// Does this array have the same number of elements? or, does
// this object have the same number of properties?
} else if (constructor_a === Array) {
len_a = a.length;
if (len_a !== b.length) { return false; }
for (i = 0; i < len_a; i += 1) {
if (recurse(a[i], b[i]) === false) { return false; }
}
} else if (constructor_a === Object) {
for (i in a) {
if (!b[i]) { return false; }
if (recurse(a[i], b[i]) === false) { return false; }
}
}
// OK.
// We threw our best at it, and it didn't budge. It's probably equal.
return true;
};
}());
BELT.clone = BELT.core.clone = (function() {
// Private helper function for public clone() API.
function recurse(from_i, doppel_i) {
// isObject will catch arrays and objects, both of which we
// need to loop on.
if (BELT.core.isObject(from_i)) {
return BELT.core.clone(from_i, doppel_i);
} else {
return from_i;
}
}
return function clone(from, useHard) {
var doppel,
len,
i;
if (BELT.core.isArray(from)) {
doppel = [];
len = from.length;
for (i = 0; i < len; i += 1) {
doppel[i] = recurse(from[i], doppel[i]);
}
} else if (BELT.core.isObject(from)) {
doppel = {};
for (i in from) {
doppel[i] = recurse(from[i], doppel[i]);
}
} else {
doppel = from;
}
return doppel;
};
}());
// How to use to write plugins:
// BELT.extend(BELT.namespace('ns'), { props });
// jQuery(target, obj1)...
// BELT.extend();
// @dep isArray, isObject, isFunction, report, clone
// `object` is a crappy argument name.
BELT.extend = BELT.core.extend = function(target, object, useHard) {
var clone,
extended,
dex,
key,
new_key,
len,
i;
// By default, hard overwrite of target properties is disabled.
useHard = useHard || false;
// If it's a function.
// Pass in the prototype, but return the function.
BELT.core.isFunction(target) &&
(extended = target, target = target.prototype);
// Error checking.
// ONLY check that target is a FN or object, and that
// `object` is an object. (might be array or obj).
!BELT.core.isObject(target) && !BELT.core.isFunction(target) &&
BELT.problem.report({});
!BELT.core.isObject(object) && BELT.problem.report({});
if (BELT.core.isArray(object)) {
// Merge Arrays if `target` and `object` are both arrays.
// This generally does a standard array merge:
//
// a = [1,2] , b = [2,3], a merge b => [1,2,3]
//
// But if you have hard writing enabled, this will actually
// produce MESH instances of a and b into a with clones.
//
// a = [{animal:panda}], b = [{animal:fox}].
// a merge b => [{animal:fox}].
// a[0].animal = "artic fox";
// a[0].animal => "artic fox", b[0].animal => "fox"
//
// Useful if you want to produce clones of stuff in b and
// mutate them safely elsewhere.
//
if (BELT.core.isArray(target)) {
console.log("Extending array with array...");
len = object.length;
for (i = 0; i < len; i += 1) {
if (useHard) {
if (target[i] === object[i]) { continue; }
target[i] = BELT.core.clone(object[i]);
} else {
// Index of any matches.
dex = target.indexOf(object[i]);
// If it's in there, continue.
// If you don't want that, use concat().
if (dex > 0) { continue; }
// Add a clone into target.
target.push(BELT.core.clone(object[i]));
}
}
return target;
} else {
console.log("Extending object with array...");
key = new_key = 'Array';
i = 2;
while (target[new_key] !== undefined) {
console.log('entering while loop');
if (BELT.core.areArraysEqual(target[new_key], object)) {
return target;
}
new_key = key + i;
i += 1;
}
target[new_key] = BELT.core.clone(object);
return target;
}
} else if (BELT.core.isObject(object)) {
console.log("We're extending target with an object.");
console.log("This counts when you pass in a function or object as the target.");
// CAUTION:
// This will not match object-to-object equivalence.
// It will only capture whether an intance of what you've
// passed in is already FOUND within the array, not if there
// is an object EQUAL TO the instance you're merging in.
//
// Don't be surprised.
//
// If you're looking for a strict test of whether objects are
// equal, _.isEqual() in the underscore library will do that.
if (BELT.core.isArray(target)){
console.log('Extending an array with an object...');
console.warn('WARNING: see documentation for core.extend() for behavior.');
console.warn('Currently, this functions more like push() than not.');
if (target.indexOf(object) > -1) {
return target;
}
target.push(BELT.core.clone(object));
return target;
}
else {
console.log('Extending object with an object...');
// Mesh properties of object with target.
// This is a more traditional `extend` function.
}
}
// Output target.
//console.error('WARNING: BELT.extend() incomplete.');
return extended || target;
};
BELT.namespace = BELT.core.namespace = function(str) {
var levels = str && str.split && str.split('.'),
i = 0,
name_space,
step,
msg,
len;
// Fallthrough if incorrect usage.
if (levels === undefined) {
msg = 'BELT.namespace(str) : str can\'t be ' + str;
throw new Error(msg);
}
// Find the namespace (should be first str.)
name_space = levels[0];
step = global[levels[0]] || (global[levels[0]] = {});
levels.shift();
// Iterate through the levels of the namespace string.
len = levels.length;
for (; i < len; i += 1) {
if (typeof step[levels[i]] === 'undefined') {
step[levels[i]] = {};
} else if (typeof step[levels[i]] === 'function') {
i += 1;
msg = 'BELT.namespace() : ' + name_space + '.';
msg += levels.slice(0, i).join('.') + ' is a function';
throw new Error(msg);
}
step = step[levels[i]];
}
return step;
};
BELT.inherit = BELT.core.inherit = (function(){
var F = function(){};
return function inherit(P, C) {
var opt = typeof P === 'object' ?
P :
{ parent:P, child:C};
F.prototype = opt.parent.prototype;
opt.child.prototype = new F();
opt.child.prototype.constructor = opt.child;
};
}());
BELT.core.createConsoleAlias = function(){
var new_console = new function BELTConsole() {},
g_console = BELT.storage.get('global_console'),
method_groups = {
ie_opera_ff_webkit: ['log', 'warn', 'error', 'info', 'assert', 'clear'],
opera_ff_webkit: ['dir', 'trace', 'profile', 'profileEnd', 'group', 'groupEnd'],
ff_webkit: ['debug', 'groupCollapsed', 'time', 'timeEnd', 'noSuchMethod'],
webkit: ['timeStamp', 'markTimeline', 'dirxml']
},
group,
method,
len,
fn,
i;
for (group in method_groups) {
len = method_groups[group].length;
for (i = 0; i < len; i += 1) {
new_console[method_groups[group][i]] = (function(method) {
// Must be called directly on console object, otherwise you
// have IllegalInvocation errors.
if (g_console[method]) {
fn = function(a) { g_console[method](a) };
} else if (g_console['log']) {
fn = function(a) { g_console['log'](a) };
} else {
fn = function(){};
}
return fn;
}(method_groups[group][i]));
}
}
// Allow direct access to private Webkit Console APIs that aren't methods.
new_console.memory = g_console.memory || {};
new_console.profiles = g_console.profiles || {};
// Return aliased functions.
return new_console;
};
/*
* DATE
*
* General purpose date handling module.
*
* timestamp()
* Returns a unix timestamp.
*
*/
BELT.date = {};
BELT.timestamp = BELT.date.timestamp = function() {
return Math.floor(Date.now() / 1000);
};
/*
* PROBLEM
*
* Error handling for your JS app.
*
* reportProblem()
* Used for logging problems. ...?
* @arguments : message [, type][, fn_name]
* { message:, [,type:] [,fn_name:] }
* @return :
*
* Err
* @dep : Belt.date.timestamp
* @arguments : message [, fn_name]
* { message: [, fn_name:] }
*/
BELT.problem = {}; // namespace.
// Public API.
BELT.reportProblem = BELT.problem.report = function(message, type, fn_name) {
var opt = (typeof message === 'object') ?
message :
{message: message, type: type, fn_name: fn_name};
// Mandatory Arg: message.
opt.message === undefined &&
BELT.problem.report({ /* !!! Error message API */ });
// Processing ...
console.error('Error: BELT.problem.report() incomplete.');
};
// ERRORS
// 1. Setting the prototype to an error instance directly does not help; the problem
// is that the trace starts with this file and not the actual location
// that the error happens.
// 2. This implementation of stack can also cause an enormous perf
// hit--up to 50% slower with stack evaluation inside BELT than
// the native implementation.
//
// HYPOTHESES
// 1. Removing `stack` call during instantiation will improve performance.
// > TRUE
// 2. Moving properties to a prototype will improve performance.
// > TRUE 2x - 4x gains.
// http://jsperf.com/belt-error-perf/4 ... 360,000 Safari
// http://jsperf.com/belt-error-perf/5 ... 1,499,000 Safari
//
//
// OBSERVATIONS
// 1. BELT.inherit() is slower with the direct-prototype inheritance.
// > 799,000 vs 812,000 OPS
//
//
BELT.problem.Error = function(message) {
this.message = message || '';
this._e = new Error();
this.timestamp = BELT.date.timestamp();
};
BELT.problem.Error.prototype = new Error();
BELT.problem.Error.prototype.constructor = BELT.problem.Error;
BELT.problem.Error.prototype.stack = undefined;
BELT.problem.Error.prototype.getStack = function(){
this.stack = this.stack || this._e.stack;
return this.stack;
};
BELT.problem.Error.prototype.navigator_raw = global.navigator || 'Unavailable.';
BELT.problem.Error.prototype.location_raw = global.location || 'Unavailable.';
/*
* STORAGE (CACHE)
*
* storage.set()
* Set a key-value pair in the private _cache object.
* @arguments : key, value / {key:, value:}
* @returns : true if success
*
* storage.get()
* Retreive an item from the private _cache object.
* @arguments : key [string] / [empty]
* @return : _cache.key / clone of _cache
*
*/
BELT.storage = {};
BELT.storage.set = function(key, value) {
var opt = (typeof key === 'object') ?
key :
{key: key, value: value};
// Arg checking: overkill, but better than losing data.
opt.key === undefined &&
BELT.problem.report({/* !!! Error message API */});
typeof opt.key !== 'string' &&
BELT.problem.report({ /* !!! Error message API */});
opt.value === undefined &&
BELT.problem.report({ /* !!! Error message API */ });
// Set to private cache.
_cache[opt.key] = opt.value;
};
BELT.storage.get = function(key) {
if (key) { return _cache[key] }
else { return BELT.core.clone(_cache) }
};
/* ----------- bottom ------------- */
/*
* EXPOSE/EXPORT
*
* Create a safe console alias.
*
* Expose local BELT variable to the global namespace, based on the
* global variable passed into this self-invoking function.
*
*/
global.console = BELT.core.createConsoleAlias();
global.BELT = BELT;
}(window)); // Will either be GLOBAL (node) || window (browser)
</script>
var
x = {
a: [],
b: {
one: "one",
two: {
point1: 2.1,
point2: 2.2,
point3: 2.3
},
three: [3.1, 3.2, 3.3, 3.4]
},
c: function(b) { b = 1; return b },
d: "okay",
e: 1
},
x2 = {
a: [],
b: {
one: "one",
two: {
point1: 2.1,
point2: 2.2,
point3: 2.3
},
three: [3.1, 3.2, 3.3, 3.4]
},
c: function(b) { b = 1; return b },
d: "okay",
e: 1
},
y = {
a: [],
b: {
one: "one",
two: {
point1: 2.1,
point2: 2.2,
point3: 2.3
},
three: [3.1, 3.2, 3.3, 3.4]
},
c: function(b) { b = 1; return b },
d: "okay",
e: 2
},
z = {
a: [],
b: {
one: "one",
two: {
point1: 2.1,
point2: 2.2,
point3: 2.3
},
three: [3.1, 3.2, 3.3, 3.4]
},
c: function(b) { b = 2; return b },
d: "okay",
e: 2
};
Ready to run.
Test | Ops/sec | |
---|---|---|
Belt.core.isEqual (d4) |
| ready |
_.isEqual |
| ready |
BELT.core.isEqual (d6) |
| ready |
BELT.core.isEqual (d7) |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.