对象的比较方法

一、前言

对象的比较是我们经常遇到的情况。本文来看看,对象之间应该如何比较呢?

1、例子

css 复制代码
const a = {
    num: '1'
}
const b = {
    num: '1'
}

const result1 = (a === b)
const result2 = (a == b)

按我们的平时的逻辑,我们会认为变量a和b应该相同吧。

可现实是: result1的结果是false, 但也可以理解,因为===是严格比较,变量a和b是不同的实例变量,内存位置指向也不同,结果是false也正常。

那result2呢,结果为什么也是false。

因为平时我们比较对象,不会在意变量的内存地址是否相同, 而通过严格相等运算符===和宽松相等运算符==来进行对象比较,都是通过指针指向的内存地址来做比较的。

但是在平时我们的业务中,基本上只要两个对象的内容是一致的,或者说某个属性值(如id)相同,我们就会把两个对象认为是相同的。

这就引出了不同的比较方法。

二、比较方式

1、严格比较----判断是否同个实例

也就是上述的例子,严格相等运算符===和宽松相等运算符==。

Object.is()方法用来判断两个值是否严格相等。它与严格比较运算符(===)的行为基本一致。 Object.is()方法在以下情况会返回true。

  1. 两个值都是undefined。
  2. 两个值都是null。
  3. 两个值都是NaN。
  4. 两个值都是true或false。
  5. 两个值都是相同个数的字符并且按照相同顺序组成的字符串。
  6. 两个值都指向同一个对象。
  7. 两个值都是正零(+0)、都是负零(-0)和都是除零和NaN外的其他同一个数字。
css 复制代码
用法:
Object.is(a, b);

2、仅比较某个属性值,手动实现

如果仅仅是比较某个值,可以封装成简单的函数,传入比较的对象和比较的属性名,即可简单返回布尔值。

python 复制代码
function isEqual(object1, object2, str) {
  return object1[str] === object2[str] 
}

isEqual(a, b, 'num')

3、多属性比较时,浅层比较

想要比较两个对象内容是否一致,思路是要遍历对象的所有键名和键值是否都一致:

1、判断两个对象是否指向同一内存

2、使用Object.getOwnPropertyNames/Object.keys获取对象所有键名数组

3、判断两个对象的键名数组是否相等

4、遍历键名,判断键值是否都相等

4、多属性而且属性又嵌套对象后,深层递归比较

深层递归比较,其实跟浅层的逻辑差不多,不过就是增加了递归的逻辑。

不用for循环的原因,下面是示例的代码,对象属性的顺序也必须一致,才会返回相同。

所以我选择用for + hasOwnProperty的方式进行编码。

javascript 复制代码
// 对比对象是否相同
export const isObjectValueEqual = (a, b) => {
  // 判断两个对象是否指向同一内存,指向同一内存返回true
  if (a === b) return true 
  // 获取两个对象键值数组
  // const aProps = Object.getOwnPropertyNames(a)
  // const bProps = Object.getOwnPropertyNames(b)
  const aProps = Object.keys(a)
  const bProps = Object.keys(b)
  console.log(aProps, bProps)
  // 判断两个对象键值数组长度是否一致,不一致返回false
  if (aProps.length !== bProps.length) return false
  // 遍历对象的键值
  for (const prop in a) {
    // 判断a的键值,在b中是否存在,不存在,返回false
    if (b.hasOwnProperty(prop)) {
      // 判断a的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回false
      if (typeof a[prop] === 'object') {
        if (!isObjectValueEqual(a[prop], b[prop])) return false
      } else if (a[prop] !== b[prop]) {
        return false
      }
    } else {
      return false
    }
  }
  return true
}

注意Object.getOwnPropertyNames有坑。

特别是在vue项目中,父组件与子组件进行传递数据时,数据对象会多出一个属性__ob__: Observer,此属性是vue框架对数据设置的监控器,一般都是不可枚举的。而使用Object.getOwnPropertyNames(obj).forEach遍历对象去遍历父组件传来的对象时,会遍历出不可枚举对象,导致实际遍历结果数据量增多,此时,换成Objec.keys(obj).forEach()遍历就不会遍历到这个不可枚举属性;

正常我两个对象都是14个键名,

用object.keys遍历是14个

用object.getOwnPropertyNames遍历则多了一个,就是 ob 属性,多枚举了一个则导致了返回结果错误。

三、小结

上述方式也只是冰山一角,比较方式和实现方式还有很多,欢迎jym各显神通。

ps: 我是地霊殿__三無,希望能帮到你。

相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom9 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试