index.js
1 var pSlice = Array.prototype.slice; 2 var Object_keys = typeof Object.keys === 'function' 3 ? Object.keys 4 : function (obj) { 5 var keys = []; 6 for (var key in obj) keys.push(key); 7 return keys; 8 } 9 ; 10 11 var deepEqual = module.exports = function (actual, expected) { 12 // enforce Object.is +0 !== -0 13 if (actual === 0 && expected === 0) { 14 return areZerosEqual(actual, expected); 15 16 // 7.1. All identical values are equivalent, as determined by ===. 17 } else if (actual === expected) { 18 return true; 19 20 } else if (actual instanceof Date && expected instanceof Date) { 21 return actual.getTime() === expected.getTime(); 22 23 } else if (isNumberNaN(actual)) { 24 return isNumberNaN(expected); 25 26 // 7.3. Other pairs that do not both pass typeof value == 'object', 27 // equivalence is determined by ==. 28 } else if (typeof actual != 'object' && typeof expected != 'object') { 29 return actual == expected; 30 31 // 7.4. For all other Object pairs, including Array objects, equivalence is 32 // determined by having the same number of owned properties (as verified 33 // with Object.prototype.hasOwnProperty.call), the same set of keys 34 // (although not necessarily the same order), equivalent values for every 35 // corresponding key, and an identical 'prototype' property. Note: this 36 // accounts for both named and indexed properties on Arrays. 37 } else { 38 return objEquiv(actual, expected); 39 } 40 }; 41 42 function isUndefinedOrNull(value) { 43 return value === null || value === undefined; 44 } 45 46 function isArguments(object) { 47 return Object.prototype.toString.call(object) == '[object Arguments]'; 48 } 49 50 function isNumberNaN(value) { 51 // NaN === NaN -> false 52 return typeof value == 'number' && value !== value; 53 } 54 55 function areZerosEqual(zeroA, zeroB) { 56 // (1 / +0|0) -> Infinity, but (1 / -0) -> -Infinity and (Infinity !== -Infinity) 57 return (1 / zeroA) === (1 / zeroB); 58 } 59 60 function objEquiv(a, b) { 61 if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) 62 return false; 63 64 // an identical 'prototype' property. 65 if (a.prototype !== b.prototype) return false; 66 //~~~I've managed to break Object.keys through screwy arguments passing. 67 // Converting to array solves the problem. 68 if (isArguments(a)) { 69 if (!isArguments(b)) { 70 return false; 71 } 72 a = pSlice.call(a); 73 b = pSlice.call(b); 74 return deepEqual(a, b); 75 } 76 try { 77 var ka = Object_keys(a), 78 kb = Object_keys(b), 79 key, i; 80 } catch (e) {//happens when one is a string literal and the other isn't 81 return false; 82 } 83 // having the same number of owned properties (keys incorporates 84 // hasOwnProperty) 85 if (ka.length != kb.length) 86 return false; 87 //the same set of keys (although not necessarily the same order), 88 ka.sort(); 89 kb.sort(); 90 //~~~cheap key test 91 for (i = ka.length - 1; i >= 0; i--) { 92 if (ka[i] != kb[i]) 93 return false; 94 } 95 //equivalent values for every corresponding key, and 96 //~~~possibly expensive deep test 97 for (i = ka.length - 1; i >= 0; i--) { 98 key = ka[i]; 99 if (!deepEqual(a[key], b[key])) return false; 100 } 101 return true; 102 }