deep-compare
今天分享的是一个可以进行深度比较传入参数的方法。在日常的开发中,比较两个数据是否相等的情况非常多,对于基本数据类型的相对简单。但是当比较两个对象的时候就比较复杂了。下面分享一个在 stackoverflow 上看到的方法,可以比较传入的多个参数是否相等。
在阅读代码时加入了自己的一些注释,同时理了一下对比两个不知道数据类型的变量时,该如何去做。具体涉及到鉴别时各种条件的优先顺序以及需要注意的点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | function deepCompare () { var i, l, leftChain, rightChain; function compare2Objects (x, y) { var p; // remember that NaN === NaN returns false // and isNaN(undefined) returns true // 第一步如果两个是 NaN 返回 true。要注意如何识别 NaN,即上边的两条注释 if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') { return true; } // Compare primitives and functions. // Check if both arguments link to the same object. // Especially useful on the step where we compare prototypes // 第二步直接判断是否相等 if (x === y) { return true; } // Works in case when functions are created in constructor. // Comparing dates is a common scenario. Another built-ins? // We can even handle functions passed across iframes // 第三步,如果是函数或者是通过 new 出来的 String,它们的类型为 object ,调用他们的 toString 来检测 if ((typeof x === 'function' && typeof y === 'function') || (x instanceof Date && y instanceof Date) || (x instanceof RegExp && y instanceof RegExp) || (x instanceof String && y instanceof String) || (x instanceof Number && y instanceof Number)) { return x.toString() === y.toString(); } // 第四步,到这里只剩下对象(包括{}, [],注意不包含 null,因为 null === null 为 true)没有判断出来 // At last checking prototypes as good as we can if (!(x instanceof Object && y instanceof Object)) { return false; } /* var a = function fun () {} var b = new a a.prototype.isPrototypeOf(b) // true */ if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) { return false; } if (x.constructor !== y.constructor) { return false; } if (x.prototype !== y.prototype) { return false; } // Check for infinitive linking loops if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) { return false; } // Quick checking of one object being a subset of another. // todo: cache the structure of arguments[0] for performance // 这里只是检测了两个对象(数组)拥有相同的属性,并且属性类型相同,具体属性值还没有深度对比 for (p in y) { if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { return false; } else if (typeof y[p] !== typeof x[p]) { return false; } } for (p in x) { // 首先反过来来了一遍上面(61-68)循环对比,避免 x 中包含的某个属性 y 中没有,不可以删除 if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { return false; } else if (typeof y[p] !== typeof x[p]) { return false; } switch (typeof (x[p])) { case 'object': case 'function': leftChain.push(x); rightChain.push(y); // 递归 if (!compare2Objects (x[p], y[p])) { return false; } leftChain.pop(); rightChain.pop(); break; default: if (x[p] !== y[p]) { return false; } break; } } return true; } if (arguments.length < 1) { return true; //Die silently? Don't know how to handle such case, please help... // throw "Need two or more arguments to compare"; } for (i = 1, l = arguments.length; i < l; i++) { leftChain = []; //Todo: this can be cached rightChain = []; if (!compare2Objects(arguments[0], arguments[i])) { return false; } } return true; } |