JS 实现对象的浅比较

641 阅读2分钟

浅比较(也叫做引用比较),在js中 === 进行的比较就是浅比较,如果左右两边对象是是同一个对象的引用,则返回 true.

一般来说,深浅比较的区别在于对复杂类型的处理:

  • 浅比较:
    • 基本类型会比较值是否相等,复杂类型会比较引用地址是否相等
  • 深比较:
    • 递归遍历里层的每一个属性值是否相等(比较原值相是否等,不关注是否是同一引用)

在实现之前,先来看看下面这段代码中,对象的引用对于结果的影响:

console.log({a:1} === {a:1}) // false 
const obj1 = {a:1}; 
const obj2 = obj1; 
console.log(obj1 === obj2) //true

image.png

const child = {
    x:1
}
const obj1 = {
    a: 1,
    b: child,
    arr:[100,200,300]
}
const obj2 = {
    a: 1,
    b: child,
    arr:[100,200,300]
}
console.log(obj1 === obj2) // false
console.log(obj1.a === obj2.a) // true
console.log(obj1.b === obj2.b) // true
console.log(obj1.arr === obj2.arr) // false

运行结果:

image.png

实现浅比较

因为深浅比较主要是对对象结构进行比较,所以需要除了非空校验,还要先判断数据类型(如果函数也归为对象,可以使用正则进行匹配)

判断是否是对象:

const isObj = function isObj(obj){
    // return obj !== null && typeof obj === 'object'
    return obj !== null && /^(object|function)$/.test(typeof obj)  // 把函数也归为对象
}

浅比较实现:

  • 判断数据是否是对象
  • 获取两个对象的所有 key,使用Reflect.ownKeys()
    • Object.keys(): 返回属性数组,不包括不可枚举的属性
    • Reflect.ownKeys():包括不可枚举的属性Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)
  • 判断上一步生成的两个key值数组的长度,如果相等进行下一步
  • 进行浅比较(只比较一层)
    • 如果某个key值只存在于其中一个数组中,false
    • 如果两个对象中 key 相同,值不同,false

代码实现:

const shallowEqual = function shallowEqual(obj1,obj2){
    if(!isObj(obj1) || !isObj(obj2)) return false  // 不是对象
    let keys1 = Reflect.ownKeys(obj1)
    let keys2 = Reflect.ownKeys(obj2)
    if(keys1.length !== keys2.length) return false // 两个对象长度不同,不相等
    //  浅比较
    for(let i=0;i<keys1.length;i++) {
        let key = keys1[i]
        if(!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) return false
    }
    return true
}