Add fast path for objects with a single primitive value back to _.matches and add isStrictComparable helper to reduce _.matches and _.isEqual.

This commit is contained in:
John-David Dalton
2014-07-10 09:26:20 -07:00
parent 884c2af17d
commit 1631ceedb3
2 changed files with 44 additions and 35 deletions

View File

@@ -2893,6 +2893,18 @@
: (value && type == 'object' && reHostCtor.test(toString.call(value))) || false; : (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;
} }
/**
* Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` if suitable for strict
* equality comparisons, else `false`.
*/
function isStrictComparable(value) {
return value === value && (value === 0 ? (1 / value > 0) : !isObject(value));
}
/** /**
* Creates a clone of the given array buffer. * Creates a clone of the given array buffer.
* *
@@ -6963,23 +6975,9 @@
*/ */
function isEqual(value, other, customizer, thisArg) { function isEqual(value, other, customizer, thisArg) {
customizer = typeof customizer == 'function' && baseCallback(customizer, thisArg, 3); customizer = typeof customizer == 'function' && baseCallback(customizer, thisArg, 3);
return (!customizer && isStrictComparable(value) && isStrictComparable(other))
if (!customizer) { ? value === other
// exit early for identical values : baseIsEqual(value, other, customizer);
if (value === other) {
// treat `-0` vs. `+0` as not equal
return value !== 0 || (1 / value == 1 / other);
}
var valType = typeof value,
othType = typeof other;
// exit early for unlike primitive values
if (value === value && (value == null || other == null ||
(valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object'))) {
return false;
}
}
return baseIsEqual(value, other, customizer);
} }
/** /**
@@ -8542,17 +8540,28 @@
*/ */
function matches(source) { function matches(source) {
var props = keys(source), var props = keys(source),
length = props.length, length = props.length;
index = length,
modes = Array(length), if (length == 1) {
var key = props[0],
value = source[key];
if (isStrictComparable(value)) {
return function(object) {
return object != null && value === object[key] && hasOwnProperty.call(object, key);
};
}
}
var index = length,
flags = Array(length),
vals = Array(length); vals = Array(length);
while (index--) { while (index--) {
var value = source[props[index]], value = source[props[index]];
isDeep = value !== value || (value === 0 && 1 / value < 0) || isObject(value); var isStrict = isStrictComparable(value);
modes[index] = isDeep; flags[index] = isStrict;
vals[index] = isDeep ? baseClone(value, isDeep) : value; vals[index] = isStrict ? value : baseClone(value, false);
} }
return function(object) { return function(object) {
index = length; index = length;
@@ -8560,13 +8569,13 @@
return !index; return !index;
} }
while (index--) { while (index--) {
if (modes[index] ? !hasOwnProperty.call(object, props[index]) : vals[index] !== object[props[index]]) { if (flags[index] ? vals[index] !== object[props[index]] : !hasOwnProperty.call(object, props[index])) {
return false; return false;
} }
} }
index = length; index = length;
while (index--) { while (index--) {
if (modes[index] ? !baseIsEqual(vals[index], object[props[index]], null, true) : !hasOwnProperty.call(object, props[index])) { if (flags[index] ? !hasOwnProperty.call(object, props[index]) : !baseIsEqual(vals[index], object[props[index]], null, true)) {
return false; return false;
} }
} }

View File

@@ -6721,24 +6721,24 @@
strictEqual(matches(otherObject), false); strictEqual(matches(otherObject), false);
}); });
test('should not change match behavior if `source` is augmented', 6, function() { test('should not change match behavior if `source` is augmented', 9, function() {
_.each([{ 'a': 1, 'b': 2 }, { 'a': { 'b': 2, 'c': 3 } }], function(source, index) { _.each([{ 'a': { 'b': 2, 'c': 3 } }, { 'a': 1, 'b': 2 }, { 'a': 1 }], function(source, index) {
var object = _.cloneDeep(source), var object = _.cloneDeep(source),
matches = _.matches(source); matches = _.matches(source);
strictEqual(matches(object), true, 'a' + index); strictEqual(matches(object), true);
if (index) { if (index) {
source.a.b = 1;
source.a.c = 2;
source.a.d = 3;
} else {
source.a = 2; source.a = 2;
source.b = 1; source.b = 1;
source.c = 3; source.c = 3;
} else {
source.a.b = 1;
source.a.c = 2;
source.a.d = 3;
} }
strictEqual(matches(object), true, 'b' + index); strictEqual(matches(object), true);
strictEqual(matches(source), false, 'c' + index); strictEqual(matches(source), false);
}); });
}); });