From d4fac798fc607bb0c6f0c5ec749064aca89a7dc5 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sat, 22 Feb 2014 17:08:28 -0800 Subject: [PATCH] Optimize `_.isEqual` for primitives. --- lodash.js | 31 +++++++++++++++++++++---------- perf/perf.js | 18 +++++++++++++++++- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/lodash.js b/lodash.js index 2b9e35e6d..40edf54f4 100644 --- a/lodash.js +++ b/lodash.js @@ -1462,7 +1462,6 @@ * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ function baseIsEqual(a, b, callback, isWhere, stackA, stackB) { - // used to indicate that when comparing objects, `a` has at least the properties of `b` if (callback) { var result = callback(a, b); if (typeof result != 'undefined') { @@ -1478,16 +1477,10 @@ otherType = typeof b; // exit early for unlike primitive values - if (a === a && - !(a && (type == 'function' || type == 'object')) && - !(b && (otherType == 'function' || otherType == 'object'))) { + if (a === a && (a == null || b == null || + (type != 'function' && type != 'object' && otherType != 'function' && otherType != 'object'))) { return false; } - // exit early for `null` and `undefined` avoiding ES3's Function#call behavior - // http://es5.github.io/#x15.3.4.4 - if (a == null || b == null) { - return a === b; - } // compare [[Class]] names var className = toString.call(a), otherClass = toString.call(b); @@ -6025,7 +6018,24 @@ * // => true */ function isEqual(a, b, callback, thisArg) { - return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2)); + callback = typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2); + + if (!callback) { + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + var type = typeof a, + otherType = typeof b; + + // exit early for unlike primitive values + if (a === a && (a == null || b == null || + (type != 'function' && type != 'object' && otherType != 'function' && otherType != 'object'))) { + return false; + } + } + return baseIsEqual(a, b, callback); } /** @@ -7107,6 +7117,7 @@ if (!hasOwnProperty.call(object, key)) { return false; } + // treat `+0` vs. `-0` as not equal var b = object[key]; return a === b && (a !== 0 || (1 / a == 1 / b)); }; diff --git a/perf/perf.js b/perf/perf.js index 91f798908..b4dbec08f 100644 --- a/perf/perf.js +++ b/perf/perf.js @@ -1268,7 +1268,23 @@ /*--------------------------------------------------------------------------*/ suites.push( - Benchmark.Suite('`_.isEqual` comparing primitives and objects (edge case)') + Benchmark.Suite('`_.isEqual` comparing primitives') + .add(buildName, { + 'fn': '\ + lodash.isEqual(1, "1");\ + lodash.isEqual(1, 1)', + 'teardown': 'function isEqual(){}' + }) + .add(otherName, { + 'fn': '\ + _.isEqual(1, "1");\ + _.isEqual(1, 1);', + 'teardown': 'function isEqual(){}' + }) + ); + + suites.push( + Benchmark.Suite('`_.isEqual` comparing primitives and their object counterparts (edge case)') .add(buildName, { 'fn': 'lodash.isEqual(objectOfPrimitives, objectOfObjects)', 'teardown': 'function isEqual(){}'