判断对象相等

127 阅读2分钟

一、基础概念:对象相等的类型

  1. 值相等(Value Equality):对象属性值相同,不要求引用相同
  2. 引用相等(Reference Equality):两个变量指向同一内存地址

二、引用相等:简单判断

1. =====(基本类型适用)
const a = { x: 1 };
const b = a;
console.log(a === b); // true(引用相等)

const c = { x: 1 };
console.log(a === c); // false(值相等但引用不同)
  • 注意== 会类型转换,=== 严格比较,对象比较需用 ===

三、值相等:深度比较方案

1. 浅拷贝 + 递归比较(手动实现)
function isDeepEqual(obj1, obj2) {
  // 处理基本类型和null
  if (obj1 === obj2) return true;
  if (obj1 === null || obj2 === null) return false;
  if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
  
  // 处理数组
  if (Array.isArray(obj1) && Array.isArray(obj2)) {
    if (obj1.length !== obj2.length) return false;
    return obj1.every((val, i) => isDeepEqual(val, obj2[i]));
  }
  
  // 处理普通对象
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) return false;
  
  return keys1.every(key => isDeepEqual(obj1[key], obj2[key]));
}
  • 缺陷:无法处理循环引用(如 obj.a = obj
2. JSON序列化方案(简单场景)
function isShallowEqual(obj1, obj2) {
  return JSON.stringify(obj1) === JSON.stringify(obj2);
}
  • 局限
    • 无法处理函数、RegExp等非JSON类型
    • 会忽略undefinednull的差异
    • 无法检测循环引用
3. 第三方库:Lodash _.isEqual
import { isEqual } from 'lodash';

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
console.log(isEqual(obj1, obj2)); // true
  • 优势
    • 支持循环引用检测
    • 处理函数、Date等特殊类型
    • 性能优化(短路比较、缓存等)

四、问题

1. 问:为什么 {a:1} == {a:1} 返回 false?
    • ===== 比较对象时,比较的是内存引用而非值
    • 两个对象是独立创建的,引用地址不同,故返回false
2. 问:如何判断两个数组是否相等?
    • 浅比较:JSON.stringify(arr1) === JSON.stringify(arr2)(忽略顺序时无效)
    • 深比较:
      arr1.length === arr2.length && arr1.every((val, i) => val === arr2[i]);
      
    • 有序数组:使用递归或Lodash的isEqual

五、性能优化与特殊场景

1. 快速比较:先检查引用和长度
function optimizedEqual(obj1, obj2) {
  if (obj1 === obj2) return true;
  if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
  // 再进行深度比较...
}
2. 循环引用处理
function deepEqualWithCycle(obj1, obj2, visited = new WeakMap()) {
  if (obj1 === obj2) return true;
  if (visited.has(obj1)) return visited.get(obj1) === obj2;
  
  visited.set(obj1, obj2);
  // 递归比较...
}
3. React场景:浅比较优化
// shouldComponentUpdate中
import { shallowEqual } from 'react-redux';

shouldComponentUpdate(nextProps) {
  return !shallowEqual(this.props, nextProps);
}

六、方案对比与场景选择

方案优点缺点适用场景
===性能极高仅比较引用基本类型或引用相等
JSON序列化代码简洁功能有限简单对象浅比较
手动递归可控性强需处理循环引用自定义场景
Lodash isEqual功能全面引入额外依赖通用场景