最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript判斷對(duì)象相等性的多種方式

 更新時(shí)間:2025年12月09日 08:33:10   作者:之恒君  
文章總結(jié)了JavaScript中判斷對(duì)象相等的多種方法,包括相等性比較運(yùn)算符、深度比較方法、淺比較、使用第三方庫(kù)、ES6+新特性、特殊對(duì)象比較以及性能比較,每種方法都有其特點(diǎn)和適用場(chǎng)景,選擇合適的方法可以提高代碼的性能和可維護(hù)性,需要的朋友可以參考下

在 JavaScript 中,判斷對(duì)象相等有多種方式,各有不同用途和特點(diǎn)。

1. 相等性比較運(yùn)算符

1.1 嚴(yán)格相等===和 寬松相等==

const obj1 = { a: 1 };
const obj2 = { a: 1 };
const obj3 = obj1;

console.log(obj1 === obj2);  // false
console.log(obj1 === obj3);  // true
console.log(obj1 == obj2);   // false
console.log(obj1 == obj3);   // true

特點(diǎn)

  • 比較的是引用地址,不是內(nèi)容
  • 只有指向同一內(nèi)存地址的對(duì)象才相等
  • 適用于判斷是否是同一個(gè)對(duì)象

2. 深度比較方法

2.1 JSON.stringify(簡(jiǎn)單對(duì)象)

function simpleDeepEqual(obj1, obj2) {
  return JSON.stringify(obj1) === JSON.stringify(obj2);
}

const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
const obj3 = { b: 2, a: 1 };  // 屬性順序不同
const obj4 = { a: 1, b: null };

console.log(simpleDeepEqual(obj1, obj2));  // true
console.log(simpleDeepEqual(obj1, obj3));  // false!順序不同
console.log(simpleDeepEqual(obj1, { a: 1, b: 2 }));  // true

局限性

  • 屬性順序敏感
  • 不能處理 undefined、function、Symbol、循環(huán)引用
  • undefined會(huì)被忽略,NaN會(huì)變成 null
  • 日期對(duì)象會(huì)轉(zhuǎn)為字符串
// JSON.stringify 的問(wèn)題
console.log(JSON.stringify({ a: undefined }));  // "{}"
console.log(JSON.stringify({ a: function() {} }));  // "{}"
console.log(JSON.stringify({ a: Symbol() }));  // "{}"
console.log(JSON.stringify({ a: NaN }));  // '{"a":null}'
console.log(JSON.stringify(new Date()));  // 日期字符串

2.2 遞歸深度比較

function deepEqual(obj1, obj2) {
  // 1. 基本類型比較
  if (obj1 === obj2) return true;
  
  // 2. 檢查 null
  if (obj1 == null || obj2 == null) return false;
  
  // 3. 檢查類型
  if (typeof obj1 !== typeof obj2) return false;
  if (obj1.constructor !== obj2.constructor) return false;
  
  // 4. 特殊類型處理
  if (obj1 instanceof Date && obj2 instanceof Date) {
    return obj1.getTime() === obj2.getTime();
  }
  
  if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
    return obj1.toString() === obj2.toString();
  }
  
  // 5. 數(shù)組比較
  if (Array.isArray(obj1)) {
    if (obj1.length !== obj2.length) return false;
    for (let i = 0; i < obj1.length; i++) {
      if (!deepEqual(obj1[i], obj2[i])) return false;
    }
    return true;
  }
  
  // 6. 對(duì)象比較
  if (typeof obj1 === 'object') {
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);
    
    if (keys1.length !== keys2.length) return false;
    
    for (let key of keys1) {
      if (!obj2.hasOwnProperty(key)) return false;
      if (!deepEqual(obj1[key], obj2[key])) return false;
    }
    
    return true;
  }
  
  // 7. 其他類型
  return false;
}

優(yōu)化版本

function deepEqual(obj1, obj2, visited = new WeakMap()) {
  // 處理循環(huán)引用
  if (visited.has(obj1) && visited.get(obj1) === obj2) {
    return true;
  }
  visited.set(obj1, obj2);
  
  // 基本類型
  if (obj1 === obj2) return true;
  if (obj1 == null || obj2 == null) return false;
  if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
    return obj1 === obj2;
  }
  
  // 不同類型
  if (obj1.constructor !== obj2.constructor) return false;
  
  // 特殊對(duì)象
  if (obj1 instanceof Date) return obj1.getTime() === obj2.getTime();
  if (obj1 instanceof RegExp) return obj1.toString() === obj2.toString();
  if (obj1 instanceof Map || obj2 instanceof Map) {
    if (obj1.size !== obj2.size) return false;
    for (let [key, val] of obj1) {
      if (!obj2.has(key)) return false;
      if (!deepEqual(val, obj2.get(key), visited)) return false;
    }
    return true;
  }
  if (obj1 instanceof Set || obj2 instanceof Set) {
    if (obj1.size !== obj2.size) return false;
    for (let val of obj1) {
      if (!obj2.has(val)) return false;
    }
    return true;
  }
  
  // 普通對(duì)象/數(shù)組
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  
  if (keys1.length !== keys2.length) return false;
  
  for (let key of keys1) {
    if (!obj2.hasOwnProperty(key)) return false;
    if (!deepEqual(obj1[key], obj2[key], visited)) return false;
  }
  
  return true;
}

3. 淺比較

3.1 淺比較(只比較第一層)

function shallowEqual(obj1, obj2) {
  if (obj1 === obj2) return true;
  
  if (typeof obj1 !== 'object' || obj1 === null ||
      typeof obj2 !== 'object' || obj2 === null) {
    return false;
  }
  
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  
  if (keys1.length !== keys2.length) return false;
  
  for (let key of keys1) {
    if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
      return false;
    }
  }
  
  return true;
}

// 使用
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };

console.log(shallowEqual(obj1, obj2));  // false(b 屬性引用不同)
console.log(shallowEqual(obj1, obj1));  // true

3.2 React 風(fēng)格的淺比較

function reactShallowEqual(objA, objB) {
  if (Object.is(objA, objB)) return true;
  
  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false;
  }
  
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);
  
  if (keysA.length !== keysB.length) return false;
  
  for (let i = 0; i < keysA.length; i++) {
    if (
      !Object.prototype.hasOwnProperty.call(objB, keysA[i]) ||
      !Object.is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }
  
  return true;
}

4. 使用第三方庫(kù)

4.1 Lodash

// 安裝: npm install lodash
import _ from 'lodash';

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };

console.log(_.isEqual(obj1, obj2));  // true
console.log(_.isEqualWith(obj1, obj2, customizer));  // 自定義比較

4.2 deep-diff

// 安裝: npm install deep-diff
import diff from 'deep-diff';

const differences = diff(obj1, obj2);
if (!differences) {
  console.log('對(duì)象相同');
} else {
  console.log('差異:', differences);
}

5. ES6+ 新特性比較

5.1 Object.is

// Object.is 用于比較值
console.log(Object.is(0, -0));       // false
console.log(Object.is(NaN, NaN));    // true
console.log(Object.is({}, {}));      // false(引用比較)

// 與 === 的區(qū)別
console.log(+0 === -0);              // true
console.log(NaN === NaN);            // false
console.log(Object.is(+0, -0));      // false
console.log(Object.is(NaN, NaN));    // true

5.2 Map 和 Set

// Set 自動(dòng)去重
const set1 = new Set([1, 2, 3]);
const set2 = new Set([1, 2, 3]);
console.log(set1 === set2);  // false
console.log(_.isEqual(set1, set2));  // true

// Map
const map1 = new Map([['a', 1]]);
const map2 = new Map([['a', 1]]);
console.log(map1 === map2);  // false
console.log(_.isEqual(map1, map2));  // true

6. 特殊對(duì)象比較

6.1 函數(shù)比較

function func1() { return 1; }
function func2() { return 1; }
function func3 = func1;

console.log(func1 === func2);  // false
console.log(func1 === func3);  // true
console.log(func1.toString() === func2.toString());  // true(不推薦)

// 函數(shù)比較實(shí)用方法
function functionsEqual(fn1, fn2) {
  return fn1.toString() === fn2.toString();
}

6.2 類實(shí)例比較

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  equals(other) {
    if (!(other instanceof Person)) return false;
    return this.name === other.name && this.age === other.age;
  }
}

const p1 = new Person('Alice', 30);
const p2 = new Person('Alice', 30);
const p3 = new Person('Bob', 25);

console.log(p1 === p2);      // false
console.log(p1.equals(p2));  // true
console.log(p1.equals(p3));  // false

7. 性能比較

function performanceTest() {
  const obj1 = { a: 1, b: 2, c: { d: 3, e: { f: 4 } } };
  const obj2 = { a: 1, b: 2, c: { d: 3, e: { f: 4 } } };
  const obj3 = { a: 1, b: 2, c: { d: 3, e: { f: 5 } } };
  
  const iterations = 10000;
  
  // 1. JSON.stringify
  console.time('JSON.stringify');
  for (let i = 0; i < iterations; i++) {
    JSON.stringify(obj1) === JSON.stringify(obj2);
  }
  console.timeEnd('JSON.stringify');
  
  // 2. 遞歸深度比較
  console.time('遞歸深度比較');
  for (let i = 0; i < iterations; i++) {
    deepEqual(obj1, obj2);
  }
  console.timeEnd('遞歸深度比較');
  
  // 3. Lodash
  console.time('Lodash');
  for (let i = 0; i < iterations; i++) {
    _.isEqual(obj1, obj2);
  }
  console.timeEnd('Lodash');
  
  // 4. 淺比較
  console.time('淺比較');
  for (let i = 0; i < iterations; i++) {
    shallowEqual(obj1, obj2);
  }
  console.timeEnd('淺比較');
}

// 結(jié)果通常:
// 淺比較最快
// JSON.stringify 次之
// Lodash 再次
// 遞歸深度比較最慢

8. 實(shí)用工具函數(shù)

8.1 通用深度比較

function isDeepEqual(obj1, obj2, options = {}) {
  const {
    strict = true,           // 嚴(yán)格比較
    checkSymbols = false,     // 檢查 Symbol
    skipKeys = [],            // 跳過(guò)的鍵
  } = options;
  
  // 快速路徑
  if (obj1 === obj2) return true;
  if (obj1 == null || obj2 == null) return false;
  
  const type1 = typeof obj1;
  const type2 = typeof obj2;
  if (type1 !== type2) return false;
  
  // 基本類型
  if (type1 !== 'object' && type1 !== 'function') {
    if (strict) {
      return Object.is(obj1, obj2);
    }
    return obj1 == obj2;
  }
  
  // 構(gòu)造函數(shù)比較
  if (obj1.constructor !== obj2.constructor) return false;
  
  // 特殊對(duì)象處理
  if (obj1 instanceof Date) {
    return obj1.getTime() === obj2.getTime();
  }
  
  if (obj1 instanceof RegExp) {
    return obj1.toString() === obj2.toString();
  }
  
  if (obj1 instanceof Map) {
    if (obj1.size !== obj2.size) return false;
    for (let [key, val] of obj1) {
      if (!obj2.has(key) || !isDeepEqual(val, obj2.get(key), options)) {
        return false;
      }
    }
    return true;
  }
  
  if (obj1 instanceof Set) {
    if (obj1.size !== obj2.size) return false;
    for (let val of obj1) {
      if (!obj2.has(val)) return false;
    }
    return true;
  }
  
  // 收集所有鍵
  const keys1 = [...Object.keys(obj1)];
  const keys2 = [...Object.keys(obj2)];
  
  if (checkSymbols) {
    keys1.push(...Object.getOwnPropertySymbols(obj1));
    keys2.push(...Object.getOwnPropertySymbols(obj2));
  }
  
  // 過(guò)濾跳過(guò)的鍵
  const filteredKeys1 = keys1.filter(key => !skipKeys.includes(key));
  const filteredKeys2 = keys2.filter(key => !skipKeys.includes(key));
  
  if (filteredKeys1.length !== filteredKeys2.length) return false;
  
  for (let key of filteredKeys1) {
    if (!obj2.hasOwnProperty(key)) return false;
    if (!isDeepEqual(obj1[key], obj2[key], options)) return false;
  }
  
  return true;
}

8.2 比較指定路徑

function compareByPath(obj1, obj2, paths) {
  for (let path of paths) {
    const val1 = get(obj1, path);
    const val2 = get(obj2, path);
    if (!isDeepEqual(val1, val2)) {
      return false;
    }
  }
  return true;
}

function get(obj, path) {
  return path.split('.').reduce((acc, key) => acc && acc[key], obj);
}

// 使用
const obj1 = { a: 1, b: { c: 2, d: 3 }, e: 4 };
const obj2 = { a: 1, b: { c: 2, d: 5 }, e: 4 };

console.log(compareByPath(obj1, obj2, ['a', 'b.c']));  // true
console.log(compareByPath(obj1, obj2, ['a', 'b.d']));  // false

9. 常見(jiàn)場(chǎng)景

9.1 React/PureComponent

// React 的 shouldComponentUpdate
class MyComponent extends React.PureComponent {
  // 自動(dòng)進(jìn)行淺比較
}

// 自定義 shouldComponentUpdate
class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState);
  }
}

9.2 Redux

// Redux reducer
function reducer(state = initialState, action) {
  switch (action.type) {
    case 'UPDATE_DATA':
      // 深度比較
      if (_.isEqual(state.data, action.data)) {
        return state;  // 相同,不更新
      }
      return { ...state, data: action.data };
    default:
      return state;
  }
}

9.3 緩存(Memoization)

// 使用深度比較的緩存
function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    
    for (let [cachedKey, cachedValue] of cache) {
      if (isDeepEqual(JSON.parse(cachedKey), args)) {
        return cachedValue;
      }
    }
    
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

10. 最佳實(shí)踐總結(jié)

場(chǎng)景推薦方法原因
比較引用是否相同===Object.is最快,最直接
簡(jiǎn)單對(duì)象深度比較JSON.stringify簡(jiǎn)單,但有限制
復(fù)雜對(duì)象深度比較Lodash 的 _.isEqual可靠,功能完整
性能關(guān)鍵,比較第一層淺比較最快,適合不可變數(shù)據(jù)
自定義比較邏輯自定義比較函數(shù)靈活可控
比較類實(shí)例實(shí)現(xiàn) equals方法面向?qū)ο?,語(yǔ)義清晰

選擇指南

  1. 引用比較用 ===值比較用深度比較
  2. 性能優(yōu)先用淺比較,精度優(yōu)先用深度比較
  3. 生產(chǎn)環(huán)境用 Lodash簡(jiǎn)單場(chǎng)景用 JSON.stringify
  4. 特殊對(duì)象要特殊處理(Date、RegExp、Map、Set 等)
  5. 注意邊界情況(循環(huán)引用、Symbol、undefined 等)

根據(jù)具體需求選擇合適的方法,平衡性能、準(zhǔn)確性和可維護(hù)性。

以上就是JavaScript判斷對(duì)象相等性的多種方式的詳細(xì)內(nèi)容,更多關(guān)于JavaScript對(duì)象相等性判斷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JS回調(diào)函數(shù)簡(jiǎn)單易懂的入門實(shí)例分析

    JS回調(diào)函數(shù)簡(jiǎn)單易懂的入門實(shí)例分析

    這篇文章主要介紹了JS回調(diào)函數(shù),結(jié)合簡(jiǎn)單實(shí)例形式分析了javascript回調(diào)函數(shù)的概念、原理、相關(guān)操作技巧與使用注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • p5.js用cylinder()繪制3D圓柱體的詳細(xì)步驟

    p5.js用cylinder()繪制3D圓柱體的詳細(xì)步驟

    p5.js是一個(gè)開(kāi)放源碼的 JavaScript 庫(kù),旨在讓編程變得更為簡(jiǎn)單,特別是對(duì)于視覺(jué)藝術(shù)和創(chuàng)意表達(dá),這篇文章主要介紹了p5.js用cylinder()繪制3D圓柱體的詳細(xì)步驟,需要的朋友可以參考下
    2025-08-08
  • JavaScript編寫推箱子游戲

    JavaScript編寫推箱子游戲

    本文給大家介紹的是使用javascript來(lái)實(shí)現(xiàn)一款經(jīng)典的老游戲--推箱子,主要側(cè)重于實(shí)現(xiàn)的思路,最后附上源碼給大家。
    2015-07-07
  • javascript實(shí)現(xiàn)自動(dòng)輸出文本(打字特效)

    javascript實(shí)現(xiàn)自動(dòng)輸出文本(打字特效)

    文字如何實(shí)現(xiàn)打字的效果呢?在瀏覽網(wǎng)頁(yè)的時(shí)候也經(jīng)常能看到這種效果。本文給大家匯總介紹了幾種打字效果的文字特效,文字一個(gè)一個(gè)地打印在頁(yè)面上。
    2015-08-08
  • javascript自執(zhí)行函數(shù)之偽命名空間封裝法

    javascript自執(zhí)行函數(shù)之偽命名空間封裝法

    比較之后,我們可以發(fā)現(xiàn),第二方法更加的直觀,易于理解。但是少了封裝過(guò)程,代碼完全裸露在外。
    2010-12-12
  • 微信小程序分包操作實(shí)戰(zhàn)指南

    微信小程序分包操作實(shí)戰(zhàn)指南

    分包指的是把一個(gè)完整的小程序項(xiàng)目,按照需求劃分為不同的子包, 在構(gòu)建時(shí)打包成不同的分包,用戶在使用時(shí)按需進(jìn)行加載,下面這篇文章主要給大家介紹了關(guān)于微信小程序分包操作的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • JavaScript實(shí)現(xiàn)的仿新浪微博原生態(tài)輸入字?jǐn)?shù)即時(shí)檢查功能【兼容IE6】

    JavaScript實(shí)現(xiàn)的仿新浪微博原生態(tài)輸入字?jǐn)?shù)即時(shí)檢查功能【兼容IE6】

    這篇文章主要介紹了JavaScript實(shí)現(xiàn)的仿新浪微博原生態(tài)輸入字?jǐn)?shù)即時(shí)檢查功能,涉及javascript事件響應(yīng)及字符串的遍歷、轉(zhuǎn)換、判斷等相關(guān)操作技巧,需要的朋友可以參考下
    2017-09-09
  • 微信小程序自定義導(dǎo)航的方法

    微信小程序自定義導(dǎo)航的方法

    這篇文章主要為大家詳細(xì)介紹了微信小程序自定義導(dǎo)航的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • JavaScript WeakMap使用詳解

    JavaScript WeakMap使用詳解

    這篇文章主要介紹了JavaScript WeakMap使用的詳細(xì)介紹,幫助大家更好的理解和使用JavaScript,感興趣的朋友可以了解下
    2021-02-02
  • 微信小程序連續(xù)簽到7天積分獲得功能的示例代碼

    微信小程序連續(xù)簽到7天積分獲得功能的示例代碼

    今天通過(guò)一個(gè)案例給大家分享微信小程序連續(xù)簽到7天積分獲得功能,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考價(jià)值,感興趣的朋友一起學(xué)習(xí)吧
    2020-08-08

最新評(píng)論

肇州县| 咸宁市| 开化县| 武冈市| 通化市| 筠连县| 铁岭县| 循化| 象州县| 许昌市| 芦溪县| 阳东县| 祁东县| 西华县| 上思县| 荆门市| 景洪市| 纳雍县| 玛多县| 朝阳市| 新巴尔虎右旗| 江都市| 罗平县| 张家川| 武义县| 邳州市| 山阴县| 武穴市| 庆安县| 南宁市| 永丰县| 屯门区| 纳雍县| 忻州市| 大荔县| 东至县| 偃师市| 乡城县| 商丘市| 方城县| 黔东|