| 11067 | |
| 11068 | // Internal recursive comparison function. |
| 11069 | function eq(a, b, stack) { |
| 11070 | // Identical objects are equal. `0 === -0`, but they aren't identical. |
| 11071 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. |
| 11072 | if (a === b) return a !== 0 || 1 / a == 1 / b; |
| 11073 | // A strict comparison is necessary because `null == undefined`. |
| 11074 | if (a == null || b == null) return a === b; |
| 11075 | // Unwrap any wrapped objects. |
| 11076 | if (a._chain) a = a._wrapped; |
| 11077 | if (b._chain) b = b._wrapped; |
| 11078 | // Invoke a custom `isEqual` method if one is provided. |
| 11079 | if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b); |
| 11080 | if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a); |
| 11081 | // Compare `[[Class]]` names. |
| 11082 | var className = toString.call(a); |
| 11083 | if (className != toString.call(b)) return false; |
| 11084 | switch (className) { |
| 11085 | // Strings, numbers, dates, and booleans are compared by value. |
| 11086 | case '[object String]': |
| 11087 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is |
| 11088 | // equivalent to `new String("5")`. |
| 11089 | return a == String(b); |
| 11090 | case '[object Number]': |
| 11091 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for |
| 11092 | // other numeric values. |
| 11093 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); |
| 11094 | case '[object Date]': |
| 11095 | case '[object Boolean]': |
| 11096 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their |
| 11097 | // millisecond representations. Note that invalid dates with millisecond representations |
| 11098 | // of `NaN` are not equivalent. |
| 11099 | return +a == +b; |
| 11100 | // RegExps are compared by their source patterns and flags. |
| 11101 | case '[object RegExp]': |
| 11102 | return a.source == b.source && |
| 11103 | a.global == b.global && |
| 11104 | a.multiline == b.multiline && |
| 11105 | a.ignoreCase == b.ignoreCase; |
| 11106 | } |
| 11107 | if (typeof a != 'object' || typeof b != 'object') return false; |
| 11108 | // Assume equality for cyclic structures. The algorithm for detecting cyclic |
| 11109 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. |
| 11110 | var length = stack.length; |
| 11111 | while (length--) { |
| 11112 | // Linear search. Performance is inversely proportional to the number of |
| 11113 | // unique nested structures. |
| 11114 | if (stack[length] == a) return true; |
| 11115 | } |
| 11116 | // Add the first object to the stack of traversed objects. |
| 11117 | stack.push(a); |
| 11118 | var size = 0, result = true; |
| 11119 | // Recursively compare objects and arrays. |
| 11120 | if (className == '[object Array]') { |
| 11121 | // Compare array lengths to determine if a deep comparison is necessary. |
| 11122 | size = a.length; |
| 11123 | result = size == b.length; |
| 11124 | if (result) { |
| 11125 | // Deep compare the contents, ignoring non-numeric properties. |
| 11126 | while (size--) { |