两个月以前在公众号发过一个图片消息,标题是 How to compare two objects in JavaScript,有一个关注了我的同事第二天告诉我说看不懂。看不懂是结果,而为什么看不懂则是导致这一结果的过程。我试着揣测了她看不懂的过程,大概有这些原因:
这是站在读者角度的分析。若是站在笔者角度,最大的问题就是:只有代码没有注释。当然了,这个锅我是不背的,毕竟这类消息的目标用户从不是包含着上述三个特征的读者。 而现在我把这个问题又拎了出来,强化一下记忆。
说了这么多废话,到底如何比较呢?
===
大法好能想到的第一个方法必然是全等比较,如果obj_1 === obj_2
这条表达式返回的结果是 true 的话,则说明两个对象的内存地址相同,即:本就是一个对象。在 JavaScript 中,只要不是NaN
,一个变量总是和自身相等的。
如果不全等呢?接下来就要凭借着对 Object 对象的了解,手动比较了。
在 JavaScript 中,函数也是对象的一种,所以我们先考虑一下,如果要比较的是两个函数该怎么办。
回忆一下你是如何区分两个函数的。
看函数名,看参数,看函数中的语句。如果我们能把函数转换成所有内容组成的字符串,是不是就很直观了?
所以在这里,我们只需要调用toString
方法,将结果进行比较即可。
除了函数之外,同样符合object
身份的Date
对象也需要用特殊的办法进行比较。
这个倒也简单,将两者用getTime
方法转换成时间戳,再进行比较,即可。
这是个老命题了。
因为对象的可继承属性,决定了一个对象不止有自己内部定义的key-value
对,如果需要的话,还要考虑到对象原形链上可访问到的属性。
可以用Object.getPrototypeOf
方法获得一个对象的原型(这里说“原型”是翻译自函数名,但不太准确,“父对象”更为合理,二者是继承关系),再将获得的对象进行比较。
这也是我们要做的最后一步:看对象的每一个键值对是否相等。
key
;key
对应的value
是否相等;key
对应的value
是对象,递归function isDeepEqual(obj1, obj2, testPrototypes = false) {
if (obj1 === obj2) {
return true;
}
if (typeof obj1 === 'function' && typeof obj2 === 'function') {
return obj1.toString() === obj2.toString();
}
if (obj1 instanceof Date && obj2 instanceof Date) {
return obj1.getTime() && obj2.getTime();
}
const isPrototypeEqual = testPrototypes
? isDeepEqual(
Object.getPrototypeOf(obj1),
Object.getPrototypeOf(obj2),
true
)
: true;
const obj1Props = Object.getOwnPropertyNames(obj1);
const obj2Props = Object.getOwnPropertyNames(obj2);
return (
obj1Props.length === obj2Props.length &&
isPrototypeEqual &&
obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop]))
);
}