mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-11 11:27:50 +00:00
Ensure _.isEqual works correctly for objects from another document and add _.clone benchmark.
Former-commit-id: b1ef745ec6c24e8ea0c8fae304ead80c60dfd5aa
This commit is contained in:
7
build.js
7
build.js
@@ -881,14 +881,17 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// remove JScript [[DontEnum]] fix from `_.isEqual`
|
// remove JScript [[DontEnum]] fix from `_.isEqual`
|
||||||
source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(result *&& *hasDontEnumBug[\s\S]+?\n\1}/, '');
|
source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(hasDontEnumBug[\s\S]+?\n\1}/, '');
|
||||||
|
|
||||||
// remove IE `shift` and `splice` fix from mutator Array functions mixin
|
// remove IE `shift` and `splice` fix from mutator Array functions mixin
|
||||||
source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(value.length *=== *0[\s\S]+?\n\1}/, '');
|
source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(value.length *=== *0[\s\S]+?\n\1}/, '');
|
||||||
|
|
||||||
// remove `noArgsClass` from `_.clone` and `_.size`
|
// remove `noArgsClass` from `_.clone`, `_.isEqual`, and `_.size`
|
||||||
source = source.replace(/ *\|\| *\(noArgsClass *&[^)]+?\)\)/g, '');
|
source = source.replace(/ *\|\| *\(noArgsClass *&[^)]+?\)\)/g, '');
|
||||||
|
|
||||||
|
// remove `noArgsClass` from `_.isEqual`
|
||||||
|
source = source.replace(/if *\(noArgsClass[^}]+?}\n/, '');
|
||||||
|
|
||||||
// remove `noArraySliceOnStrings` from `_.toArray`
|
// remove `noArraySliceOnStrings` from `_.toArray`
|
||||||
source = source.replace(/noArraySliceOnStrings *\?[^:]+: *([^)]+)/g, '$1');
|
source = source.replace(/noArraySliceOnStrings *\?[^:]+: *([^)]+)/g, '$1');
|
||||||
|
|
||||||
|
|||||||
@@ -218,13 +218,18 @@
|
|||||||
|
|
||||||
// manually convert `arrayLikeClasses` property assignments because
|
// manually convert `arrayLikeClasses` property assignments because
|
||||||
// Closure Compiler errors trying to minify them
|
// Closure Compiler errors trying to minify them
|
||||||
source = source.replace(/(arrayLikeClasses =)[\s\S]+?= *true/g, "$1{'[object Arguments]': true, '[object Array]': true, '[object String]': true }");
|
source = source.replace(/(arrayLikeClasses =)[\s\S]+?= *true/g,
|
||||||
|
"$1{'[object Arguments]': true, '[object Array]': true, '[object Boolean]': false, " +
|
||||||
|
"'[object Date]': false, '[object Function]': false, '[object Number]': false, " +
|
||||||
|
"'[object Object]': false, '[object RegExp]': false, '[object String]': true }"
|
||||||
|
);
|
||||||
|
|
||||||
// manually convert `cloneableClasses` property assignments because
|
// manually convert `cloneableClasses` property assignments because
|
||||||
// Closure Compiler errors trying to minify them
|
// Closure Compiler errors trying to minify them
|
||||||
source = source.replace(/(cloneableClasses =)[\s\S]+?= *true/g,
|
source = source.replace(/(cloneableClasses =)[\s\S]+?= *true/g,
|
||||||
"$1{'[object Array]': true, '[object Boolean]': true, '[object Date]': true, " +
|
"$1{'[object Arguments]': false, '[object Array]': true, '[object Boolean]': true, " +
|
||||||
"'[object Number]': true, '[object Object]': true, '[object RegExp]': true, '[object String]': true }"
|
"'[object Date]': true, '[object Function]': false, '[object Number]': true, " +
|
||||||
|
"'[object Object]': true, '[object RegExp]': true, '[object String]': true }"
|
||||||
);
|
);
|
||||||
|
|
||||||
// add brackets to whitelisted properties so Closure Compiler won't mung them
|
// add brackets to whitelisted properties so Closure Compiler won't mung them
|
||||||
|
|||||||
195
lodash.js
195
lodash.js
@@ -39,7 +39,10 @@
|
|||||||
|
|
||||||
/** Native prototype shortcuts */
|
/** Native prototype shortcuts */
|
||||||
var ArrayProto = Array.prototype,
|
var ArrayProto = Array.prototype,
|
||||||
ObjectProto = Object.prototype;
|
BoolProto = Boolean.prototype,
|
||||||
|
ObjectProto = Object.prototype,
|
||||||
|
NumberProto = Number.prototype,
|
||||||
|
StringProto = String.prototype;
|
||||||
|
|
||||||
/** Used to generate unique IDs */
|
/** Used to generate unique IDs */
|
||||||
var idCounter = 0;
|
var idCounter = 0;
|
||||||
@@ -141,6 +144,9 @@
|
|||||||
*/
|
*/
|
||||||
var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx';
|
var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx';
|
||||||
|
|
||||||
|
/** Detect if a node's [[Class]] is unresolvable (IE < 9) */
|
||||||
|
var noNodeClass = toString.call(window.document || {}) == objectClass;
|
||||||
|
|
||||||
/* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */
|
/* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */
|
||||||
var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera));
|
var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera));
|
||||||
|
|
||||||
@@ -161,10 +167,13 @@
|
|||||||
|
|
||||||
/** Used to identify object classifications that are array-like */
|
/** Used to identify object classifications that are array-like */
|
||||||
var arrayLikeClasses = {};
|
var arrayLikeClasses = {};
|
||||||
|
arrayLikeClasses[boolClass] = arrayLikeClasses[dateClass] = arrayLikeClasses[funcClass] =
|
||||||
|
arrayLikeClasses[numberClass] = arrayLikeClasses[objectClass] = arrayLikeClasses[regexpClass] = false;
|
||||||
arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true;
|
arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true;
|
||||||
|
|
||||||
/** Used to identify object classifications that `_.clone` supports */
|
/** Used to identify object classifications that `_.clone` supports */
|
||||||
var cloneableClasses = {};
|
var cloneableClasses = {};
|
||||||
|
cloneableClasses[argsClass] = cloneableClasses[funcClass] = false;
|
||||||
cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] =
|
cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] =
|
||||||
cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] =
|
cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] =
|
||||||
cloneableClasses[stringClass] = true;
|
cloneableClasses[stringClass] = true;
|
||||||
@@ -189,7 +198,8 @@
|
|||||||
'object': true,
|
'object': true,
|
||||||
'number': false,
|
'number': false,
|
||||||
'string': false,
|
'string': false,
|
||||||
'undefined': false
|
'undefined': false,
|
||||||
|
'unknown': true
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Used to escape characters for inclusion in compiled string literals */
|
/** Used to escape characters for inclusion in compiled string literals */
|
||||||
@@ -2494,6 +2504,8 @@
|
|||||||
* others like `_.map` without using their callback `index` argument for `deep`.
|
* others like `_.map` without using their callback `index` argument for `deep`.
|
||||||
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
|
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
|
||||||
* to avoid circular references.
|
* to avoid circular references.
|
||||||
|
* @param {Boolean} thorough Internally used to indicate whether or not to perform
|
||||||
|
* a more thorough clone of non-object values.
|
||||||
* @returns {Mixed} Returns the cloned `value`.
|
* @returns {Mixed} Returns the cloned `value`.
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -2514,8 +2526,8 @@
|
|||||||
* shallow[0] === stooges[0];
|
* shallow[0] === stooges[0];
|
||||||
* // => false
|
* // => false
|
||||||
*/
|
*/
|
||||||
function clone(value, deep, guard, stack) {
|
function clone(value, deep, guard, stack, thorough) {
|
||||||
if (!value) {
|
if (value == null) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
var isObj = typeof value == 'object';
|
var isObj = typeof value == 'object';
|
||||||
@@ -2524,34 +2536,44 @@
|
|||||||
if (guard) {
|
if (guard) {
|
||||||
deep = false;
|
deep = false;
|
||||||
}
|
}
|
||||||
|
// avoid slower checks on non-objects
|
||||||
|
if (thorough == null) {
|
||||||
|
// primitives passed from iframes use the primary document's native prototypes
|
||||||
|
thorough = !!(BoolProto.clone || NumberProto.clone || StringProto.clone);
|
||||||
|
}
|
||||||
// use custom `clone` method if available
|
// use custom `clone` method if available
|
||||||
if (value.clone && toString.call(value.clone) == funcClass) {
|
if ((isObj || thorough) && value.clone && toString.call(value.clone) == funcClass) {
|
||||||
return value.clone(deep);
|
return value.clone(deep);
|
||||||
}
|
}
|
||||||
// inspect [[Class]]
|
// inspect [[Class]]
|
||||||
if (isObj) {
|
if (isObj) {
|
||||||
var className = toString.call(value);
|
|
||||||
|
|
||||||
// don't clone `arguments` objects, functions, or non-object Objects
|
// don't clone `arguments` objects, functions, or non-object Objects
|
||||||
|
var className = toString.call(value);
|
||||||
if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) {
|
if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctor = value.constructor,
|
var useCtor,
|
||||||
isArr = className == arrayClass,
|
ctor = value.constructor,
|
||||||
useCtor = toString.call(ctor) == funcClass;
|
isArr = className == arrayClass;
|
||||||
|
|
||||||
// IE < 9 presents nodes like `Object` objects:
|
if (className == objectClass) {
|
||||||
// IE < 8 are missing the node's constructor property
|
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
|
||||||
// IE 8 node constructors are typeof "object"
|
// methods that are `typeof` "string" and still can coerce nodes to strings
|
||||||
// check if the constructor is `Object` as `Object instanceof Object` is `true`
|
isObj = !noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string');
|
||||||
if (className == objectClass &&
|
|
||||||
(isObj = useCtor && ctor instanceof ctor)) {
|
if (isObj) {
|
||||||
// An object's own properties are iterated before inherited properties.
|
// check that the constructor is `Object` because `Object instanceof Object` is `true`
|
||||||
// If the last iterated key belongs to an object's own property then
|
useCtor = toString.call(ctor) == funcClass;
|
||||||
// there are no inherited enumerable properties.
|
isObj = !useCtor || ctor instanceof ctor;
|
||||||
forIn(value, function(objValue, objKey) { isObj = objKey; });
|
}
|
||||||
isObj = isObj == true || hasOwnProperty.call(value, isObj);
|
if (isObj) {
|
||||||
|
// An object's own properties are iterated before inherited properties.
|
||||||
|
// If the last iterated key belongs to an object's own property then
|
||||||
|
// there are no inherited enumerable properties.
|
||||||
|
forIn(value, function(objValue, objKey) { isObj = objKey; });
|
||||||
|
isObj = isObj == true || hasOwnProperty.call(value, isObj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// shallow clone
|
// shallow clone
|
||||||
@@ -2596,11 +2618,11 @@
|
|||||||
if (isArr) {
|
if (isArr) {
|
||||||
var index = -1;
|
var index = -1;
|
||||||
while (++index < length) {
|
while (++index < length) {
|
||||||
result[index] = clone(value[index], deep, null, stack);
|
result[index] = clone(value[index], deep, null, stack, thorough);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
forOwn(value, function(objValue, key) {
|
forOwn(value, function(objValue, key) {
|
||||||
result[key] = clone(objValue, deep, null, stack);
|
result[key] = clone(objValue, deep, null, stack, thorough);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -2905,6 +2927,8 @@
|
|||||||
* @param {Mixed} b The other value to compare.
|
* @param {Mixed} b The other value to compare.
|
||||||
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
|
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
|
||||||
* to avoid circular references.
|
* to avoid circular references.
|
||||||
|
* @param {Boolean} thorough Internally used to indicate whether or not to perform
|
||||||
|
* a more thorough comparison of non-object values.
|
||||||
* @returns {Boolean} Returns `true` if the values are equvalent, else `false`.
|
* @returns {Boolean} Returns `true` if the values are equvalent, else `false`.
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -2917,26 +2941,33 @@
|
|||||||
* _.isEqual(moe, clone);
|
* _.isEqual(moe, clone);
|
||||||
* // => true
|
* // => true
|
||||||
*/
|
*/
|
||||||
function isEqual(a, b, stack) {
|
function isEqual(a, b, stack, thorough) {
|
||||||
stack || (stack = []);
|
stack || (stack = []);
|
||||||
|
|
||||||
// a strict comparison is necessary because `null == undefined`
|
// a strict comparison is necessary because `null == undefined`
|
||||||
if (a == null || b == null) {
|
if (a == null || b == null) {
|
||||||
return a === b;
|
return a === b;
|
||||||
}
|
}
|
||||||
// unwrap any LoDash wrapped values
|
// avoid slower checks on non-objects
|
||||||
if (a._chain) {
|
if (thorough == null) {
|
||||||
a = a._wrapped;
|
// primitives passed from iframes use the primary document's native prototypes
|
||||||
|
thorough = !!(BoolProto.isEqual || NumberProto.isEqual || StringProto.isEqual);
|
||||||
}
|
}
|
||||||
if (b._chain) {
|
if (objectTypes[typeof a] || objectTypes[typeof b] || thorough) {
|
||||||
b = b._wrapped;
|
// unwrap any LoDash wrapped values
|
||||||
}
|
if (a._chain) {
|
||||||
// use custom `isEqual` method if available
|
a = a._wrapped;
|
||||||
if (a.isEqual && toString.call(a.isEqual) == funcClass) {
|
}
|
||||||
return a.isEqual(b);
|
if (b._chain) {
|
||||||
}
|
b = b._wrapped;
|
||||||
if (b.isEqual && toString.call(b.isEqual) == funcClass) {
|
}
|
||||||
return b.isEqual(a);
|
// use custom `isEqual` method if available
|
||||||
|
if (a.isEqual && toString.call(a.isEqual) == funcClass) {
|
||||||
|
return a.isEqual(b);
|
||||||
|
}
|
||||||
|
if (b.isEqual && toString.call(b.isEqual) == funcClass) {
|
||||||
|
return b.isEqual(a);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// exit early for identical values
|
// exit early for identical values
|
||||||
if (a === b) {
|
if (a === b) {
|
||||||
@@ -2968,10 +2999,18 @@
|
|||||||
// treat string primitives and their corresponding object instances as equal
|
// treat string primitives and their corresponding object instances as equal
|
||||||
return a == b + '';
|
return a == b + '';
|
||||||
}
|
}
|
||||||
if (typeof a != 'object' || typeof b != 'object') {
|
// exit early, in older browsers, if `a` is array-like but not `b`
|
||||||
// for unequal function values
|
var isArr = arrayLikeClasses[className];
|
||||||
|
if (noArgsClass && !isArr && (isArr = isArguments(a)) && !isArguments(b)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// exit for functions and DOM nodes
|
||||||
|
if (!isArr && (className != objectClass || (noNodeClass && (
|
||||||
|
(typeof a.toString != 'function' && typeof (a + '') == 'string') ||
|
||||||
|
(typeof b.toString != 'function' && typeof (b + '') == 'string'))))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// assume cyclic structures are equal
|
// assume cyclic structures are equal
|
||||||
// the algorithm for detecting cyclic structures is adapted from ES 5.1
|
// the algorithm for detecting cyclic structures is adapted from ES 5.1
|
||||||
// section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3)
|
// section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3)
|
||||||
@@ -2990,7 +3029,7 @@
|
|||||||
stack.push(a);
|
stack.push(a);
|
||||||
|
|
||||||
// recursively compare objects and arrays (susceptible to call stack limits)
|
// recursively compare objects and arrays (susceptible to call stack limits)
|
||||||
if (arrayLikeClasses[className] || (noArgsClass && isArguments(a))) {
|
if (isArr) {
|
||||||
// compare lengths to determine if a deep comparison is necessary
|
// compare lengths to determine if a deep comparison is necessary
|
||||||
size = a.length;
|
size = a.length;
|
||||||
result = size == b.length;
|
result = size == b.length;
|
||||||
@@ -2998,54 +3037,56 @@
|
|||||||
if (result) {
|
if (result) {
|
||||||
// deep compare the contents, ignoring non-numeric properties
|
// deep compare the contents, ignoring non-numeric properties
|
||||||
while (size--) {
|
while (size--) {
|
||||||
if (!(result = isEqual(a[size], b[size], stack))) {
|
if (!(result = isEqual(a[size], b[size], stack, thorough))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// objects with different constructors are not equal
|
var ctorA = a.constructor,
|
||||||
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) {
|
ctorB = b.constructor;
|
||||||
|
|
||||||
|
// non `Object` object instances with different constructors are not equal
|
||||||
|
if (ctorA != ctorB && !(
|
||||||
|
toString.call(ctorA) == funcClass && ctorA instanceof ctorA &&
|
||||||
|
toString.call(ctorB) == funcClass && ctorB instanceof ctorB
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// deep compare objects
|
||||||
|
for (var prop in a) {
|
||||||
|
if (hasOwnProperty.call(a, prop)) {
|
||||||
|
// count the number of properties.
|
||||||
|
size++;
|
||||||
|
// deep compare each property value.
|
||||||
|
if (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack, thorough))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ensure both objects have the same number of properties
|
||||||
|
for (prop in b) {
|
||||||
|
// The JS engine in Adobe products, like InDesign, has a bug that causes
|
||||||
|
// `!size--` to throw an error so it must be wrapped in parentheses.
|
||||||
|
// https://github.com/documentcloud/underscore/issues/355
|
||||||
|
if (hasOwnProperty.call(b, prop) && !(size--)) {
|
||||||
|
// `size` will be `-1` if `b` has more properties than `a`
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// deep compare objects
|
}
|
||||||
for (var prop in a) {
|
// handle JScript [[DontEnum]] bug
|
||||||
if (hasOwnProperty.call(a, prop)) {
|
if (hasDontEnumBug) {
|
||||||
// count the number of properties.
|
while (++index < 7) {
|
||||||
size++;
|
prop = shadowed[index];
|
||||||
// deep compare each property value.
|
if (hasOwnProperty.call(a, prop) &&
|
||||||
if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) {
|
!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack, thorough))) {
|
||||||
break;
|
return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ensure both objects have the same number of properties
|
|
||||||
if (result) {
|
|
||||||
for (prop in b) {
|
|
||||||
// The JS engine in Adobe products, like InDesign, has a bug that causes
|
|
||||||
// `!size--` to throw an error so it must be wrapped in parentheses.
|
|
||||||
// https://github.com/documentcloud/underscore/issues/355
|
|
||||||
if (hasOwnProperty.call(b, prop) && !(size--)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// `size` will be `-1` if `b` has more properties than `a`
|
|
||||||
result = !size;
|
|
||||||
}
|
|
||||||
// handle JScript [[DontEnum]] bug
|
|
||||||
if (result && hasDontEnumBug) {
|
|
||||||
while (++index < 7) {
|
|
||||||
prop = shadowed[index];
|
|
||||||
if (hasOwnProperty.call(a, prop)) {
|
|
||||||
if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
27
perf/perf.js
27
perf/perf.js
@@ -132,7 +132,10 @@
|
|||||||
|
|
||||||
// potentially expensive
|
// potentially expensive
|
||||||
for (index = 0; index < this.count; index++) {
|
for (index = 0; index < this.count; index++) {
|
||||||
bindAllObjects[index] = belt.clone(lodash);
|
bindAllObjects[index] = belt.reduce(funcNames, function(object, funcName) {
|
||||||
|
object[funcName] = lodash[funcName];
|
||||||
|
return object;
|
||||||
|
}, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,6 +518,18 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
suites.push(
|
||||||
|
Benchmark.Suite('`_.clone` with an object')
|
||||||
|
.add('Lo-Dash', function() {
|
||||||
|
lodash.clone(object);
|
||||||
|
})
|
||||||
|
.add('Underscore', function() {
|
||||||
|
_.clone(object);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
suites.push(
|
suites.push(
|
||||||
Benchmark.Suite('`_.countBy` with `callback` iterating an array')
|
Benchmark.Suite('`_.countBy` with `callback` iterating an array')
|
||||||
.add('Lo-Dash', function() {
|
.add('Lo-Dash', function() {
|
||||||
@@ -1145,16 +1160,6 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
suites.push(
|
|
||||||
Benchmark.Suite('`_.size` with an array')
|
|
||||||
.add('Lo-Dash', function() {
|
|
||||||
lodash.size(numbers);
|
|
||||||
})
|
|
||||||
.add('Underscore', function() {
|
|
||||||
_.size(numbers);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
suites.push(
|
suites.push(
|
||||||
Benchmark.Suite('`_.size` with an object')
|
Benchmark.Suite('`_.size` with an object')
|
||||||
.add('Lo-Dash', function() {
|
.add('Lo-Dash', function() {
|
||||||
|
|||||||
26
test/test.js
26
test/test.js
@@ -710,9 +710,35 @@
|
|||||||
equal(_.isEqual(object, object), false);
|
equal(_.isEqual(object, object), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should use custom `isEqual` method on primitives', function() {
|
||||||
|
Boolean.prototype.isEqual = function() { return true; };
|
||||||
|
equal(_.isEqual(true, false), true);
|
||||||
|
delete Boolean.prototype.isEqual;
|
||||||
|
});
|
||||||
|
|
||||||
test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() {
|
test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() {
|
||||||
equal(_.isEqual(shadowed, {}), false);
|
equal(_.isEqual(shadowed, {}), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should return `true` for like-objects from different documents', function() {
|
||||||
|
if (window.document) {
|
||||||
|
var body = document.body,
|
||||||
|
iframe = document.createElement('iframe'),
|
||||||
|
object = { 'a': 1, 'b': 2, 'c': 3 };
|
||||||
|
|
||||||
|
body.appendChild(iframe);
|
||||||
|
var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc;
|
||||||
|
idoc.write("<script>parent._._object = { 'a': 1, 'b': 2, 'c': 3 };<\/script>");
|
||||||
|
idoc.close();
|
||||||
|
|
||||||
|
equal(_.isEqual(object, _._object), true);
|
||||||
|
body.removeChild(iframe);
|
||||||
|
delete _._object;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
skipTest();
|
||||||
|
}
|
||||||
|
});
|
||||||
}());
|
}());
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|||||||
Reference in New Issue
Block a user