在我们处理有关对象的业务逻辑的时候,你可能见过这种奇怪的现象:

compare-objects
compare-objects

即使两个不同的对象可以具有相同的属性,但在使用=====进行比较时,它们不被认为是相等的。这是因为它们是通过引用(内存中的位置)进行比较的,而不是按值进行比较的原始值。

为了测试两个对象在结构上是否相等,需要一个辅助函数。它将遍历每个对象的自身属性,以测试它们是否具有相同的值,包括嵌套对象。
可选地,对象的原型也可以通过传递true作为第三个参数来测试等价性。

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
function isDeepEqual(obj1,obj2, testPrototypes = false) {
// 如果两者类型不同,直接返回false
if (Object.prototype.toString.call(obj1) !== Object.prototype.toString.call(obj2)) {
return false
}
// 如果两者的值和类型都相等,直接返回true
if (obj1 === obj2) {
return true
}
// 如果两者都是函数则判断其转化为字符串后的值是否相等
if (typeof obj1 === 'function' && typeof obj2 === 'function') {
return obj.toString() === obj2.toString()
}
// 如果两者都是时间类型,则判断其毫秒值是否相等
if (obj1 instanceof Date && obj1 instanceof Date) {
return obj1.getTime() === obj2.getTime()
}
// 在其他情况下如果两者有一个不是对象则返回false(此时两者类型相同,判断一个就行)
if (typeof obj1 !== 'object') {
return false
}

const prototypesAreEqual = testPrototypes ?
isDeepEqual(
Object.getPrototypeOf(obj1), Object.getPrototypeOf(obj2), true
)
: true

// 其他对象(数组等)
const obj1Props = Object.getOwnProperties(obj1)
const obj2Props = Object.getOwnProperties(obj2)
return (
// 判断其属性长度是否相等
obj1Props.length === obj2Props.length &&
prototypesAreEqual &&
// 如果其属性也是对象,递归调用自身继续判断他们对应属性是否相等
obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop]))
)
}

注意:理论上此方法可以任意数据结构,但是建议用于测试普通对象、数组、函数、日期。