| 6385 | |
| 6386 | // Internal recursive comparison function for `isEqual`. |
| 6387 | var eq = function(a, b, aStack, bStack) { |
| 6388 | // Identical objects are equal. `0 === -0`, but they aren't identical. |
| 6389 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. |
| 6390 | if (a === b) return a !== 0 || 1 / a == 1 / b; |
| 6391 | // A strict comparison is necessary because `null == undefined`. |
| 6392 | if (a == null || b == null) return a === b; |
| 6393 | // Unwrap any wrapped objects. |
| 6394 | if (a instanceof _) a = a._wrapped; |
| 6395 | if (b instanceof _) b = b._wrapped; |
| 6396 | // Compare `[[Class]]` names. |
| 6397 | var className = toString.call(a); |
| 6398 | if (className != toString.call(b)) return false; |
| 6399 | switch (className) { |
| 6400 | // Strings, numbers, dates, and booleans are compared by value. |
| 6401 | case '[object String]': |
| 6402 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is |
| 6403 | // equivalent to `new String("5")`. |
| 6404 | return a == String(b); |
| 6405 | case '[object Number]': |
| 6406 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for |
| 6407 | // other numeric values. |
| 6408 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); |
| 6409 | case '[object Date]': |
| 6410 | case '[object Boolean]': |
| 6411 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their |
| 6412 | // millisecond representations. Note that invalid dates with millisecond representations |
| 6413 | // of `NaN` are not equivalent. |
| 6414 | return +a == +b; |
| 6415 | // RegExps are compared by their source patterns and flags. |
| 6416 | case '[object RegExp]': |
| 6417 | return a.source == b.source && |
| 6418 | a.global == b.global && |
| 6419 | a.multiline == b.multiline && |
| 6420 | a.ignoreCase == b.ignoreCase; |
| 6421 | } |
| 6422 | if (typeof a != 'object' || typeof b != 'object') return false; |
| 6423 | // Assume equality for cyclic structures. The algorithm for detecting cyclic |
| 6424 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. |
| 6425 | var length = aStack.length; |
| 6426 | while (length--) { |
| 6427 | // Linear search. Performance is inversely proportional to the number of |
| 6428 | // unique nested structures. |
| 6429 | if (aStack[length] == a) return bStack[length] == b; |
| 6430 | } |
| 6431 | // Add the first object to the stack of traversed objects. |
| 6432 | aStack.push(a); |
| 6433 | bStack.push(b); |
| 6434 | var size = 0, result = true; |
| 6435 | // Recursively compare objects and arrays. |
| 6436 | if (className == '[object Array]') { |
| 6437 | // Compare array lengths to determine if a deep comparison is necessary. |
| 6438 | size = a.length; |
| 6439 | result = size == b.length; |
| 6440 | if (result) { |
| 6441 | // Deep compare the contents, ignoring non-numeric properties. |
| 6442 | while (size--) { |
| 6443 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; |
| 6444 | } |