mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-10 02:47:50 +00:00
Optimize _.isEqual by using _.keys and ensure arguments objects are compared correctly.
This commit is contained in:
114
lodash.js
114
lodash.js
@@ -1278,13 +1278,16 @@
|
|||||||
return stackB[length];
|
return stackB[length];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = isArr ? Ctor(value.length) : new Ctor();
|
result = isArr ? Ctor(value.length) : new Ctor;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = isArr ? slice(value) : baseAssign({}, value);
|
result = isArr ? slice(value) : baseAssign({}, value);
|
||||||
}
|
}
|
||||||
|
if (className == argsClass || (!support.argsClass && isArguments(value))) {
|
||||||
|
result.length = value.length;
|
||||||
|
}
|
||||||
// add array properties assigned by `RegExp#exec`
|
// add array properties assigned by `RegExp#exec`
|
||||||
if (isArr) {
|
else if (isArr) {
|
||||||
if (hasOwnProperty.call(value, 'index')) {
|
if (hasOwnProperty.call(value, 'index')) {
|
||||||
result.index = value.index;
|
result.index = value.index;
|
||||||
}
|
}
|
||||||
@@ -1774,8 +1777,8 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var valClass = toString.call(value),
|
var valClass = toString.call(value),
|
||||||
othClass = toString.call(other),
|
|
||||||
valIsArg = valClass == argsClass,
|
valIsArg = valClass == argsClass,
|
||||||
|
othClass = toString.call(other),
|
||||||
othIsArg = othClass == argsClass;
|
othIsArg = othClass == argsClass;
|
||||||
|
|
||||||
if (valIsArg) {
|
if (valIsArg) {
|
||||||
@@ -1809,6 +1812,10 @@
|
|||||||
// treat string primitives and their corresponding object instances as equal
|
// treat string primitives and their corresponding object instances as equal
|
||||||
return value == String(other);
|
return value == String(other);
|
||||||
}
|
}
|
||||||
|
if (!support.argsObject) {
|
||||||
|
valIsArg = isArguments(value);
|
||||||
|
othIsArg = isArguments(other);
|
||||||
|
}
|
||||||
var isArr = arrayLikeClasses[valClass];
|
var isArr = arrayLikeClasses[valClass];
|
||||||
if (!isArr) {
|
if (!isArr) {
|
||||||
// exit for functions and DOM nodes
|
// exit for functions and DOM nodes
|
||||||
@@ -1822,10 +1829,6 @@
|
|||||||
if (valWrapped || othWrapped) {
|
if (valWrapped || othWrapped) {
|
||||||
return baseIsEqual(valWrapped ? value.__wrapped__ : value, othWrapped ? other.__wrapped__ : other, callback, isWhere, stackA, stackB);
|
return baseIsEqual(valWrapped ? value.__wrapped__ : value, othWrapped ? other.__wrapped__ : other, callback, isWhere, stackA, stackB);
|
||||||
}
|
}
|
||||||
if (!support.argsObject) {
|
|
||||||
valIsArg = isArguments(value);
|
|
||||||
othIsArg = isArguments(other);
|
|
||||||
}
|
|
||||||
var hasValCtor = !valIsArg && hasOwnProperty.call(value, 'constructor'),
|
var hasValCtor = !valIsArg && hasOwnProperty.call(value, 'constructor'),
|
||||||
hasOthCtor = !othIsArg && hasOwnProperty.call(other, 'constructor');
|
hasOthCtor = !othIsArg && hasOwnProperty.call(other, 'constructor');
|
||||||
|
|
||||||
@@ -1858,7 +1861,7 @@
|
|||||||
return stackB[length] == other;
|
return stackB[length] == other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = true;
|
var index = -1;
|
||||||
|
|
||||||
// add `value` and `other` to the stack of traversed objects
|
// add `value` and `other` to the stack of traversed objects
|
||||||
stackA.push(value);
|
stackA.push(value);
|
||||||
@@ -1866,32 +1869,60 @@
|
|||||||
|
|
||||||
// recursively compare objects and arrays (susceptible to call stack limits)
|
// recursively compare objects and arrays (susceptible to call stack limits)
|
||||||
if (isArr) {
|
if (isArr) {
|
||||||
// compare lengths to determine if a deep comparison is necessary
|
|
||||||
var othLength = other.length;
|
var othLength = other.length;
|
||||||
length = value.length;
|
length = value.length;
|
||||||
result = othLength == length;
|
result = length == othLength;
|
||||||
|
|
||||||
if (result || isWhere) {
|
if (result || isWhere) {
|
||||||
var othIndex = -1;
|
|
||||||
|
|
||||||
// deep compare the contents, ignoring non-numeric properties
|
// deep compare the contents, ignoring non-numeric properties
|
||||||
while (++othIndex < othLength) {
|
while (++index < length) {
|
||||||
var othValue = other[othIndex];
|
var valValue = value[index];
|
||||||
|
|
||||||
if (isWhere) {
|
if (isWhere) {
|
||||||
var index = -1;
|
var othIndex = -1;
|
||||||
while (++index < length) {
|
while (++othIndex < othLength) {
|
||||||
result = baseIsEqual(value[index], othValue, callback, isWhere, stackA, stackB);
|
var othValue = other[othIndex];
|
||||||
|
result = baseIsEqual(valValue, othValue, callback, isWhere, stackA, stackB);
|
||||||
if (result) {
|
if (result) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var valValue = value[othIndex];
|
othValue = other[index];
|
||||||
result = callback ? callback(valValue, othValue, othIndex) : undefined;
|
result = callback ? callback(valValue, othValue, index) : undefined;
|
||||||
result = typeof result == 'undefined'
|
if (typeof result == 'undefined') {
|
||||||
? baseIsEqual(valValue, othValue, callback, isWhere, stackA, stackB)
|
result = baseIsEqual(valValue, othValue, callback, isWhere, stackA, stackB);
|
||||||
: !!result;
|
}
|
||||||
|
if (!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var valProps = keys(value),
|
||||||
|
othProps = keys(other);
|
||||||
|
|
||||||
|
if (valIsArg) {
|
||||||
|
valProps.push('length');
|
||||||
|
}
|
||||||
|
if (othIsArg) {
|
||||||
|
othProps.push('length');
|
||||||
|
}
|
||||||
|
length = valProps.length;
|
||||||
|
result = length == othProps.length;
|
||||||
|
|
||||||
|
if (result || isWhere) {
|
||||||
|
while (++index < length) {
|
||||||
|
var key = valProps[index];
|
||||||
|
result = hasOwnProperty.call(other, key);
|
||||||
|
if (result) {
|
||||||
|
othValue = other[key];
|
||||||
|
valValue = value[key];
|
||||||
|
result = callback ? callback(valValue, othValue, key) : undefined;
|
||||||
|
if (typeof result == 'undefined') {
|
||||||
|
result = baseIsEqual(valValue, othValue, callback, isWhere, stackA, stackB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
break;
|
break;
|
||||||
@@ -1899,43 +1930,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
var size = 0;
|
|
||||||
|
|
||||||
// deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
|
|
||||||
// which, in this case, is more costly
|
|
||||||
baseForIn(other, function(othValue, key, other) {
|
|
||||||
if (hasOwnProperty.call(other, key)) {
|
|
||||||
result = false;
|
|
||||||
// count the number of properties
|
|
||||||
size++;
|
|
||||||
// deep compare each property value
|
|
||||||
if (hasOwnProperty.call(value, key)) {
|
|
||||||
var valValue = value[key];
|
|
||||||
result = callback ? callback(valValue, othValue, key) : undefined;
|
|
||||||
result = typeof result == 'undefined'
|
|
||||||
? baseIsEqual(valValue, othValue, callback, isWhere, stackA, stackB)
|
|
||||||
: !!result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && !isWhere) {
|
|
||||||
// ensure both objects have the same number of properties
|
|
||||||
baseForIn(value, function(valValue, key, value) {
|
|
||||||
if (hasOwnProperty.call(value, key)) {
|
|
||||||
// `size` will be `-1` if `value` has more properties than `other`
|
|
||||||
result = --size > -1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stackA.pop();
|
stackA.pop();
|
||||||
stackB.pop();
|
stackB.pop();
|
||||||
|
|
||||||
return result;
|
return !!result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8198,7 +8196,7 @@
|
|||||||
while (length--) {
|
while (length--) {
|
||||||
var key = props[length];
|
var key = props[length];
|
||||||
if (!(hasOwnProperty.call(object, key) &&
|
if (!(hasOwnProperty.call(object, key) &&
|
||||||
baseIsEqual(object[key], source[key], null, true))) {
|
baseIsEqual(source[key], object[key], null, true))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4661,7 +4661,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should treat `arguments` objects like `Object` objects', 2, function() {
|
test('should treat `arguments` objects like `Object` objects', 4, function() {
|
||||||
var args = (function() { return arguments; }(1, 2, 3)),
|
var args = (function() { return arguments; }(1, 2, 3)),
|
||||||
object = { '0': 1, '1': 2, '2': 3, 'length': 3 };
|
object = { '0': 1, '1': 2, '2': 3, 'length': 3 };
|
||||||
|
|
||||||
@@ -4669,12 +4669,14 @@
|
|||||||
Foo.prototype = object;
|
Foo.prototype = object;
|
||||||
|
|
||||||
strictEqual(_.isEqual(args, object), true);
|
strictEqual(_.isEqual(args, object), true);
|
||||||
|
strictEqual(_.isEqual(object, args), true);
|
||||||
|
|
||||||
if (!isPhantom) {
|
if (!isPhantom) {
|
||||||
strictEqual(_.isEqual(args, new Foo), false);
|
strictEqual(_.isEqual(args, new Foo), false);
|
||||||
|
strictEqual(_.isEqual(new Foo, args), false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
skipTest();
|
skipTest(2);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user