1 | // Taken from node's assert module, because it sucks |
---|
2 | // and exposes next to nothing useful. |
---|
3 | var util = require('./util'); |
---|
4 | |
---|
5 | module.exports = _deepEqual; |
---|
6 | |
---|
7 | var pSlice = Array.prototype.slice; |
---|
8 | |
---|
9 | function _deepEqual(actual, expected) { |
---|
10 | // 7.1. All identical values are equivalent, as determined by ===. |
---|
11 | if (actual === expected) { |
---|
12 | return true; |
---|
13 | |
---|
14 | } else if (util.isBuffer(actual) && util.isBuffer(expected)) { |
---|
15 | if (actual.length != expected.length) return false; |
---|
16 | |
---|
17 | for (var i = 0; i < actual.length; i++) { |
---|
18 | if (actual[i] !== expected[i]) return false; |
---|
19 | } |
---|
20 | |
---|
21 | return true; |
---|
22 | |
---|
23 | // 7.2. If the expected value is a Date object, the actual value is |
---|
24 | // equivalent if it is also a Date object that refers to the same time. |
---|
25 | } else if (util.isDate(actual) && util.isDate(expected)) { |
---|
26 | return actual.getTime() === expected.getTime(); |
---|
27 | |
---|
28 | // 7.3 If the expected value is a RegExp object, the actual value is |
---|
29 | // equivalent if it is also a RegExp object with the same source and |
---|
30 | // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). |
---|
31 | } else if (util.isRegExp(actual) && util.isRegExp(expected)) { |
---|
32 | return actual.source === expected.source && |
---|
33 | actual.global === expected.global && |
---|
34 | actual.multiline === expected.multiline && |
---|
35 | actual.lastIndex === expected.lastIndex && |
---|
36 | actual.ignoreCase === expected.ignoreCase; |
---|
37 | |
---|
38 | // 7.4. Other pairs that do not both pass typeof value == 'object', |
---|
39 | // equivalence is determined by ==. |
---|
40 | } else if (!util.isObject(actual) && !util.isObject(expected)) { |
---|
41 | return actual == expected; |
---|
42 | |
---|
43 | // 7.5 For all other Object pairs, including Array objects, equivalence is |
---|
44 | // determined by having the same number of owned properties (as verified |
---|
45 | // with Object.prototype.hasOwnProperty.call), the same set of keys |
---|
46 | // (although not necessarily the same order), equivalent values for every |
---|
47 | // corresponding key, and an identical 'prototype' property. Note: this |
---|
48 | // accounts for both named and indexed properties on Arrays. |
---|
49 | } else { |
---|
50 | return objEquiv(actual, expected); |
---|
51 | } |
---|
52 | } |
---|
53 | |
---|
54 | |
---|
55 | function objEquiv (a, b) { |
---|
56 | if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) |
---|
57 | return false; |
---|
58 | // an identical 'prototype' property. |
---|
59 | if (a.prototype !== b.prototype) return false; |
---|
60 | //~~~I've managed to break Object.keys through screwy arguments passing. |
---|
61 | // Converting to array solves the problem. |
---|
62 | if (util.isArguments(a)) { |
---|
63 | if (!util.isArguments(b)) { |
---|
64 | return false; |
---|
65 | } |
---|
66 | a = pSlice.call(a); |
---|
67 | b = pSlice.call(b); |
---|
68 | return _deepEqual(a, b); |
---|
69 | } |
---|
70 | try{ |
---|
71 | var ka = Object.keys(a), |
---|
72 | kb = Object.keys(b), |
---|
73 | key, i; |
---|
74 | } catch (e) {//happens when one is a string literal and the other isn't |
---|
75 | return false; |
---|
76 | } |
---|
77 | // having the same number of owned properties (keys incorporates |
---|
78 | // hasOwnProperty) |
---|
79 | if (ka.length != kb.length) |
---|
80 | return false; |
---|
81 | //the same set of keys (although not necessarily the same order), |
---|
82 | ka.sort(); |
---|
83 | kb.sort(); |
---|
84 | //~~~cheap key test |
---|
85 | for (i = ka.length - 1; i >= 0; i--) { |
---|
86 | if (ka[i] != kb[i]) |
---|
87 | return false; |
---|
88 | } |
---|
89 | //equivalent values for every corresponding key, and |
---|
90 | //~~~possibly expensive deep test |
---|
91 | for (i = ka.length - 1; i >= 0; i--) { |
---|
92 | key = ka[i]; |
---|
93 | if (!_deepEqual(a[key], b[key])) return false; |
---|
94 | } |
---|
95 | return true; |
---|
96 | } |
---|