| 16496 | |
| 16497 | // Internal recursive comparison function for `isEqual`. |
| 16498 | var eq = function(a, b, aStack, bStack) { |
| 16499 | // Identical objects are equal. `0 === -0`, but they aren't identical. |
| 16500 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. |
| 16501 | if (a === b) return a !== 0 || 1 / a == 1 / b; |
| 16502 | // A strict comparison is necessary because `null == undefined`. |
| 16503 | if (a == null || b == null) return a === b; |
| 16504 | // Unwrap any wrapped objects. |
| 16505 | if (a instanceof _) a = a._wrapped; |
| 16506 | if (b instanceof _) b = b._wrapped; |
| 16507 | // Compare `[[Class]]` names. |
| 16508 | var className = toString.call(a); |
| 16509 | if (className != toString.call(b)) return false; |
| 16510 | switch (className) { |
| 16511 | // Strings, numbers, dates, and booleans are compared by value. |
| 16512 | case '[object String]': |
| 16513 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is |
| 16514 | // equivalent to `new String("5")`. |
| 16515 | return a == String(b); |
| 16516 | case '[object Number]': |
| 16517 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for |
| 16518 | // other numeric values. |
| 16519 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); |
| 16520 | case '[object Date]': |
| 16521 | case '[object Boolean]': |
| 16522 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their |
| 16523 | // millisecond representations. Note that invalid dates with millisecond representations |
| 16524 | // of `NaN` are not equivalent. |
| 16525 | return +a == +b; |
| 16526 | // RegExps are compared by their source patterns and flags. |
| 16527 | case '[object RegExp]': |
| 16528 | return a.source == b.source && |
| 16529 | a.global == b.global && |
| 16530 | a.multiline == b.multiline && |
| 16531 | a.ignoreCase == b.ignoreCase; |
| 16532 | } |
| 16533 | if (typeof a != 'object' || typeof b != 'object') return false; |
| 16534 | // Assume equality for cyclic structures. The algorithm for detecting cyclic |
| 16535 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. |
| 16536 | var length = aStack.length; |
| 16537 | while (length--) { |
| 16538 | // Linear search. Performance is inversely proportional to the number of |
| 16539 | // unique nested structures. |
| 16540 | if (aStack[length] == a) return bStack[length] == b; |
| 16541 | } |
| 16542 | // Add the first object to the stack of traversed objects. |
| 16543 | aStack.push(a); |
| 16544 | bStack.push(b); |
| 16545 | var size = 0, result = true; |
| 16546 | // Recursively compare objects and arrays. |
| 16547 | if (className == '[object Array]') { |
| 16548 | // Compare array lengths to determine if a deep comparison is necessary. |
| 16549 | size = a.length; |
| 16550 | result = size == b.length; |
| 16551 | if (result) { |
| 16552 | // Deep compare the contents, ignoring non-numeric properties. |
| 16553 | while (size--) { |
| 16554 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; |
| 16555 | } |