两个月以前在公众号发过一个图片消息,标题是 How to compare two objects in JavaScript,有一个关注了我的同事第二天告诉我说看不懂。看不懂是结果,而为什么看不懂则是导致这一结果的过程。我试着揣测了她看不懂的过程,大概有这些原因:

  • 只有代码没有注释,阅读时心理抵触
  • 阅读时心理状态较为浮躁(这也是现在公众号读者普遍的难关)
  • 对 JavaScript Object 没有充分的理解

这是站在读者角度的分析。若是站在笔者角度,最大的问题就是:只有代码没有注释。当然了,这个锅我是不背的,毕竟这类消息的目标用户从不是包含着上述三个特征的读者。
而现在我把这个问题又拎了出来,强化一下记忆。

如何比较?

说了这么多废话,到底如何比较呢?

===大法好

能想到的第一个方法必然是全等比较,如果obj_1 === obj_2这条表达式返回的结果是 true 的话,则说明两个对象的内存地址相同,即:本就是一个对象。在 JavaScript 中,只要不是NaN,一个变量总是和自身相等的。

如果不全等呢?接下来就要凭借着对 Object 对象的了解,手动比较了。

函数比较

在 JavaScript 中,函数也是对象的一种,所以我们先考虑一下,如果要比较的是两个函数该怎么办。
回忆一下你是如何区分两个函数的。
看函数名,看参数,看函数中的语句。如果我们能把函数转换成所有内容组成的字符串,是不是就很直观了?
所以在这里,我们只需要调用toString方法,将结果进行比较即可。

时间对象的比较

除了函数之外,同样符合object身份的Date对象也需要用特殊的办法进行比较。
这个倒也简单,将两者用getTime方法转换成时间戳,再进行比较,即可。

原型比较

这是个老命题了。
因为对象的可继承属性,决定了一个对象不止有自己内部定义的key-value对,如果需要的话,还要考虑到对象原形链上可访问到的属性。
可以用Object.getPrototypeOf方法获得一个对象的原型(这里说“原型”是翻译自函数名,但不太准确,“父对象”更为合理,二者是继承关系),再将获得的对象进行比较。

键值对比较

这也是我们要做的最后一步:看对象的每一个键值对是否相等。

  1. 获取对象内所有的key;
  2. 比较每个key对应的value是否相等;
  3. 如果某个key对应的value是对象,递归

完整Demo

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]))
    );
}
关注我
⬆︎TOP