0

0

js怎么判断两个对象是否相等

煙雲

煙雲

发布时间:2025-08-17 14:30:02

|

430人浏览过

|

来源于php中文网

原创

javascript中直接使用==或===无法正确比较对象内容,因为它们只比较引用地址而非实际值;要实现内容相等判断,需进行深层比较。1. 首先检查引用是否相同,相同则返回true;2. 排除null或非对象类型,确保两者均为对象;3. 特殊处理date和regexp对象,分别比较时间戳和源码与标志;4. 对数组递归比较长度及每个元素;5. 对普通对象通过object.keys()获取自身属性并递归比较属性值;6. 使用hasownproperty确保不比较原型链上的属性。此外,实际开发中推荐使用lodash的_.isequal()或ramda的r.equals(),它们能处理循环引用、特殊类型并保证健壮性,而json.stringify()方法因属性顺序敏感、忽略undefined/函数/symbol且无法处理循环引用而不推荐使用。该方法完整支持对象、数组、date、regexp等内容一致性判断,最终返回布尔值结束。

js怎么判断两个对象是否相等

JavaScript中判断两个对象是否相等,并不能像比较基本类型那样直接使用

==
===
。这是因为在JavaScript里,对象(包括数组和函数)在内存中是按引用存储的,
==
===
比较的是这两个引用地址是否指向同一个内存空间。换句话说,即使两个对象拥有完全相同的属性和值,只要它们是不同的对象实例,直接比较的结果依然是
false
。要真正判断它们内容是否一致,我们需要进行“深层比较”,也就是逐个属性地去核对。

解决方案

要判断两个JavaScript对象是否在内容上相等,核心思路是递归地比较它们的所有属性。这包括检查它们的类型、属性数量,以及每个属性的键和值是否都匹配。如果属性值本身又是对象或数组,就需要再次递归地进行比较。

function deepEqual(obj1, obj2) {
    // 1. 快速路径:如果引用相同,那肯定是相等的
    if (obj1 === obj2) return true;

    // 2. 检查基本类型和null:如果不是对象或者其中一个是null,且引用不相同,那就不相等
    // 比如 deepEqual(null, undefined) -> false
    // deepEqual(1, '1') -> false
    if (obj1 === null || typeof obj1 !== 'object' ||
        obj2 === null || typeof obj2 !== 'object') {
        return false;
    }

    // 3. 检查Date和RegExp对象:它们有特定的比较方式
    if (obj1 instanceof Date && obj2 instanceof Date) {
        return obj1.getTime() === obj2.getTime();
    }
    if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
        return obj1.source === obj2.source && obj1.flags === obj2.flags;
    }

    // 4. 检查数组:比较长度和元素
    if (Array.isArray(obj1) && Array.isArray(obj2)) {
        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;
    }

    // 5. 检查普通对象:比较属性
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);

    if (keys1.length !== keys2.length) return false;

    // 确保obj2包含obj1的所有属性,且对应值相等
    for (const key of keys1) {
        // 使用 hasOwnProperty 确保只比较对象自身的属性,而不是原型链上的
        if (!Object.prototype.hasOwnProperty.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) {
            return false;
        }
    }

    return true;
}

// 示例用法:
const objA = { a: 1, b: { c: 2 } };
const objB = { a: 1, b: { c: 2 } };
const objC = { a: 1, b: { c: 3 } };
const arr1 = [1, { x: 10 }];
const arr2 = [1, { x: 10 }];
const arr3 = [1, { x: 20 }];

console.log('objA vs objB (deepEqual):', deepEqual(objA, objB)); // true
console.log('objA vs objC (deepEqual):', deepEqual(objA, objC)); // false
console.log('arr1 vs arr2 (deepEqual):', deepEqual(arr1, arr2)); // true
console.log('arr1 vs arr3 (deepEqual):', deepEqual(arr1, arr3)); // false
console.log('{a:1} vs {a:1} (===):', {a:1} === {a:1}); // false (引用不同)

为什么JavaScript中直接使用
==
===
无法正确比较对象?

这其实是JavaScript的一个核心概念,也是初学者常常感到困惑的地方。当你在JavaScript中创建一个对象字面量

{}
、一个数组
[]
,或者使用
new Object()
new Array()
等方式时,你实际上是在内存中开辟了一个新的空间来存储这个数据结构。变量名只是这个内存地址的一个“引用”或者说“指针”。

==
(宽松相等)和
===
(严格相等)在比较非基本类型(也就是对象、数组、函数等)时,它们的行为是一致的:它们都只比较这两个变量所指向的内存地址是否相同。

举个例子,你可能会写出这样的代码:

const user1 = { name: 'Alice', age: 30 };
const user2 = { name: 'Alice', age: 30 };
const user3 = user1;

console.log(user1 == user2); // false
console.log(user1 === user2); // false

console.log(user1 == user3); // true
console.log(user1 === user3); // true

你看,

user1
user2
尽管内容一模一样,但因为它们是在不同的时间、不同的语句中创建的,所以在内存中占据了两个不同的位置。它们是两个独立的“对象实例”,所以
==
===
都会返回
false
。而
user3 = user1
则是把
user1
的引用地址直接赋值给了
user3
,所以它们现在都指向了同一个内存地址,自然也就相等了。

这和基本类型(如字符串、数字、布尔值、

null
undefined
Symbol
BigInt
)的处理方式截然不同。基本类型在比较时,是直接比较它们的值。比如
1 == '1'
true
(因为类型转换),
1 === 1
true
。这种设计在某些场景下非常高效,但在需要内容比较时就显得有些“反直觉”了。理解这一点,是掌握JavaScript对象操作的关键一步。

实现一个可靠的深层比较函数,需要考虑哪些关键细节?

自己动手写一个深层比较函数,虽然不总是最佳实践(后面会提到库的优势),但绝对是理解JavaScript对象和递归逻辑的绝佳练习。一个“可靠”的深层比较函数,需要考虑的细节确实不少,它远不止简单地遍历属性那么简单。

首先,类型检查是基石。如果两个待比较的值类型都不同,那它们肯定不相等(除了

null == undefined
这种特例,但在严格比较中依然不相等)。我的函数开头就处理了
null
和非对象类型的情况,这是为了快速排除不匹配的场景。

接着,对特定内置对象的特殊处理。像

Date
对象,它们的值是时间戳,但直接比较对象实例肯定不行,我们需要比较它们的
getTime()
返回的时间戳。
RegExp
(正则表达式)也类似,需要比较它们的
source
flags
。这些都是
typeof
返回
object
但行为特殊的家伙。

然后是数组和普通对象的区分处理。虽然它们都是对象,但数组有

length
属性和索引访问的特性,普通对象则是键值对。我的函数里用
Array.isArray()
来区分,然后对数组逐个元素递归比较,对普通对象则遍历
Object.keys()
得到的属性名,然后递归比较属性值。

靠岸学术
靠岸学术

一款集翻译,阅读,文献管理于一体的英文文献阅读器

下载

递归是核心,也是陷阱。当一个对象的属性值又是另一个对象或数组时,我们就需要再次调用

deepEqual
函数。这种递归调用是实现“深层”比较的关键。但递归也会带来一个潜在的问题:循环引用。如果
objA
的一个属性指向
objB
,而
objB
的一个属性又指向
objA
,那么无限递归就会发生,最终导致栈溢出。一个真正健壮的深层比较函数,通常会维护一个“已访问对象”的集合(比如使用
WeakSet
),在递归过程中检查当前对象是否已经被访问过,以此来打破循环。我上面提供的简化版函数并没有处理循环引用,这是自己实现时一个常见的挑战点。

hasOwnProperty
的重要性。在遍历对象属性时,使用
Object.keys()
通常是安全的,因为它只返回对象自身的(可枚举的)属性。但如果需要更细致的控制,或者在某些旧的遍历方式中,确保只比较对象自身的属性而不是原型链上的属性,
Object.prototype.hasOwnProperty.call(obj, key)
是一个好习惯。

最后,还有一些JavaScript的“怪癖”需要考虑,比如

NaN
NaN === NaN
false
),以及
-0
+0
。一个非常严格的深层比较可能需要特殊处理这些情况,但我上面的函数遵循了
===
的行为,即
NaN
不等于
NaN
-0
等于
+0
。这些都是根据具体需求来决定的。

除了手动实现,还有哪些现成的工具或库可以帮助我们进行对象比较?

在实际项目开发中,尤其是在生产环境中,我们很少会自己从头编写一个深层比较函数,原因很简单:自己写的可能不够健壮(比如没处理循环引用、特殊对象类型、性能优化等),而且造轮子也浪费时间。这时候,成熟的第三方库就成了我们的首选。

最常用的两个库,非 LodashRamda 莫属。

  1. Lodash:它提供了非常强大的工具函数集,其中就包括

    _.isEqual()
    。这个函数功能非常全面,它能正确处理各种JavaScript值类型,包括:

    • 基本类型
    • 普通对象和数组
    • Date
      RegExp
      对象
    • 函数(比较引用)
    • NaN
      Infinity
      -0
    • 甚至能够处理循环引用(这是它比许多简易实现强大得多的地方)。

    使用起来非常简单:

    import _ from 'lodash';
    
    const objA = { a: 1, b: { c: 2 } };
    const objB = { a: 1, b: { c: 2 } };
    const circularObj1 = {};
    const circularObj2 = {};
    circularObj1.a = circularObj2;
    circularObj2.a = circularObj1; // 循环引用
    
    console.log(_.isEqual(objA, objB)); // true
    console.log(_.isEqual(circularObj1, circularObj2)); // true (Lodash 完美处理)
  2. Ramda:这是一个更注重函数式编程的库,它也提供了

    R.equals()
    R.equals
    的行为和
    _.isEqual
    类似,同样能进行深层比较,并且对各种边缘情况和循环引用有很好的支持。

    import * as R from 'ramda';
    
    const objA = { a: 1, b: { c: 2 } };
    const objB = { a: 1, b: { c: 2 } };
    
    console.log(R.equals(objA, objB)); // true

除了这些功能全面的工具库,有时你可能会看到一些简单的“hack”方法,比如:

  • JSON.stringify()
    比较
    JSON.stringify(obj1) === JSON.stringify(obj2)
    。 这种方法虽然代码量少,但非常不推荐用于通用场景。它有几个致命缺陷:
    • 属性顺序敏感
      {a:1, b:2}
      {b:2, a:1}
      字符串化后是不同的,但作为对象它们是相等的。
    • 无法处理
      undefined
      、函数、
      Symbol
      :这些类型在
      JSON.stringify
      时会被忽略或转换为
      null
      ,导致错误判断。
    • 无法处理循环引用:会直接报错。
    • 日期对象会转为字符串:比较的是字符串形式,而不是时间戳。 所以,这种方法只适用于非常简单、属性顺序固定、不含特殊类型的纯数据对象。

选择哪种方式取决于项目的需求和复杂性。对于简单的、一次性的比较,自己写一个精简版或许可以。但对于任何需要健壮性、性能和完整性保证的场景,引入 Lodash 或 Ramda 这样的成熟库,绝对是更明智、更省心的选择。它们经过了大量的测试和优化,能够应对各种复杂的对象结构。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

531

2023.06.20

正则表达式不包含
正则表达式不包含

正则表达式,又称规则表达式,,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式的文本。php中文网给大家带来了有关正则表达式的相关教程以及文章,希望对大家能有所帮助。

258

2023.07.05

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

766

2023.07.05

java正则表达式匹配字符串
java正则表达式匹配字符串

在Java中,我们可以使用正则表达式来匹配字符串。本专题为大家带来java正则表达式匹配字符串的相关内容,帮助大家解决问题。

219

2023.08.11

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

ASP 教程
ASP 教程

共34课时 | 5.9万人学习

Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号