Last active
August 29, 2015 14:02
-
-
Save yoshuawuyts/451048fed11736918443 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Internal recursive comparison function for `isEqual`. | |
var eq = function(a, b, aStack, bStack) { | |
// Identical objects are equal. `0 === -0`, but they aren't identical. | |
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). | |
if (a === b) return a !== 0 || 1 / a === 1 / b; | |
// A strict comparison is necessary because `null == undefined`. | |
if (a == null || b == null) return a === b; | |
// Unwrap any wrapped objects. | |
if (a instanceof _) a = a._wrapped; | |
if (b instanceof _) b = b._wrapped; | |
// Compare `[[Class]]` names. | |
var className = toString.call(a); | |
if (className !== toString.call(b)) return false; | |
switch (className) { | |
// Strings, numbers, regular expressions, dates, and booleans are compared by value. | |
case '[object RegExp]': | |
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') | |
case '[object String]': | |
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is | |
// equivalent to `new String("5")`. | |
return '' + a === '' + b; | |
case '[object Number]': | |
// `NaN`s are equivalent, but non-reflexive. | |
// Object(NaN) is equivalent to NaN | |
if (a != +a) return b != +b; | |
// An `egal` comparison is performed for other numeric values. | |
return a == 0 ? 1 / a == 1 / b : a == +b; | |
case '[object Date]': | |
case '[object Boolean]': | |
// Coerce dates and booleans to numeric primitive values. Dates are compared by their | |
// millisecond representations. Note that invalid dates with millisecond representations | |
// of `NaN` are not equivalent. | |
return +a === +b; | |
} | |
if (typeof a != 'object' || typeof b != 'object') return false; | |
// Assume equality for cyclic structures. The algorithm for detecting cyclic | |
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. | |
var length = aStack.length; | |
while (length--) { | |
// Linear search. Performance is inversely proportional to the number of | |
// unique nested structures. | |
if (aStack[length] === a) return bStack[length] === b; | |
} | |
// Objects with different constructors are not equivalent, but `Object`s | |
// from different frames are. | |
var aCtor = a.constructor, bCtor = b.constructor; | |
if ( | |
aCtor !== bCtor && | |
// Handle Object.create(x) cases | |
'constructor' in a && 'constructor' in b && | |
!(_.isFunction(aCtor) && aCtor instanceof aCtor && | |
_.isFunction(bCtor) && bCtor instanceof bCtor) | |
) { | |
return false; | |
} | |
// Add the first object to the stack of traversed objects. | |
aStack.push(a); | |
bStack.push(b); | |
var size, result; | |
// Recursively compare objects and arrays. | |
if (className === '[object Array]') { | |
// Compare array lengths to determine if a deep comparison is necessary. | |
size = a.length; | |
result = size === b.length; | |
if (result) { | |
// Deep compare the contents, ignoring non-numeric properties. | |
while (size--) { | |
if (!(result = eq(a[size], b[size], aStack, bStack))) break; | |
} | |
} | |
} else { | |
// Deep compare objects. | |
var keys = _.keys(a), key; | |
size = keys.length; | |
// Ensure that both objects contain the same number of properties before comparing deep equality. | |
result = _.keys(b).length == size; | |
if (result) { | |
while (size--) { | |
// Deep compare each member | |
key = keys[size]; | |
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; | |
} | |
} | |
} | |
// Remove the first object from the stack of traversed objects. | |
aStack.pop(); | |
bStack.pop(); | |
return result; | |
}; | |
// Perform a deep comparison to check if two objects are equal. | |
_.isEqual = function(a, b) { | |
return eq(a, b, [], []); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment