30-seconds-of-code:JavaScript 对象比较完全指南——从引用相等、JSON 序列化到递归深度相等
教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载导读在 JavaScript 中两个对象是否相等远没有初看起来那么简单即使两个对象拥有完全相同的属性和值与也会返回false用JSON.stringify()比较序列化字符串又暗藏多个陷阱真正可靠的方案通常需要借助递归实现深度相等比较。本文基于 30-seconds-of-code 仓库中的 object-comparison.md 展开逐层讲解引用比较、序列化比较与深度相等三种方案的原理、代码实现与边界情况并关联仓库内js/comparison系列的其他文档与源码级佐证帮助你写出可复用的对象比较工具。为什么两个内容相同的对象不相等JavaScript 中对象与数组都是引用类型reference type其相等性判断基于引用reference而非内容。这意味着只要两个对象在内存中不是同一个实例无论其属性如何相似与都会判定它们不相等const a { name: John, age: 26 }; const b { name: John, age: 26 }; a b; // false这与原始值primitive的按值比较截然不同。仓库中的姊妹篇 equality.md 详细解释了双等号宽松相等与三等号严格相等的差异会先进行类型强制转换type coercion再比较值而同时比较类型与值行为更可预测因此日常开发中应优先使用。此外还存在第三个选择Object.is()它与行为基本一致唯一的例外是NaN与0/-0的处理详见 object-is-triple-equals.md{} {}; // false Object.is({}, {}); // false对象依旧按引用比较 0 -0; // true Object.is(0, -0); // false NaN NaN; // false Object.is(NaN, NaN); // true不过要注意Object.is()同样只解决引用是否相同的问题无法比较两个内容相同但引用不同的对象。这正是下面两种方案试图解决的问题。JSON.stringify() 比较法及其陷阱JSON.stringify()经常被当作对象比较的快捷方案把两个对象序列化成字符串后再比较字符串是否相等。const equals (a, b) JSON.stringify(a) JSON.stringify(b); const a { name: John, age: 26 }; const b { name: John, age: 26 }; equals(a, b); // true const c { name: John }; const d { name: John, age: undefined }; equals(c, d); // true但按语义应该为 false上面的例子揭示了该类方案最常见的陷阱相似但不相等的值可能序列化出完全相同的字符串。undefined属性在序列化时会被直接丢弃导致{ name: John }与{ name: John, age: undefined }的序列化结果一致。除此以外JSON.stringify()比较法还存在其他已知限制键的顺序序列化结果依赖键的插入顺序数字键另有排序规则属性顺序不同但内容相同的对象会得到不同字符串函数、Symbol 等特殊值会被序列化流程跳过或处理为特殊值导致误判循环引用遇到自引用对象会抛出TypeError: Converting circular structure to JSON仓库中的 deep-clone-structured-clone.md 同样提到了这一点。因此JSON.stringify()只适合少数恰好没有上述边界情况的场景不能作为通用对象比较方案。真正的解决方案是下面这种递归深度相等比较。深度相等比较手写递归 equals()深度相等比较deep equality通过递归逐层比较两个对象每个嵌套层级的值能够覆盖空值、特殊类型、多层级嵌套等大多数场景。这是社区中各类 shallow/deep equality 工具函数普遍采用的思路。逐步拆解实现逻辑实现equals(a, b)需要依次处理以下场景步骤顺序即代码顺序第一步恒等判断identical先检查a b。这一步同时覆盖了原始值的相等判断以及两个引用指向同一个对象的情况。if (a b) return true;第二步Date 对象特殊处理如果双方都是Date实例用Date.prototype.getTime()比较其时间戳毫秒值。两个表示同一时刻但不同实例的Date对象在下不相等但时间值相等时语义上应当相等。if (a instanceof Date b instanceof Date) return a.getTime() b.getTime();第三步空值与原始值如果任意一方是null/undefined即 falsy或者双方都不是对象原始值则回退到严格比较a b。这样能正确处理原始值相等、以及null/undefined与对象混合的情况。if (!a || !b || (typeof a ! object typeof b ! object)) return a b;第四步原型检查如果两者原型prototype不同直接返回false。这一步防止把具有相同形状但来源不同的对象误判为相等。if (a.prototype ! b.prototype) return false;第五步键数量与递归使用Object.keys()获取双方的可枚举键先比较键的数量是否一致再用Array.prototype.every()遍历a的每个键确认该键在b中存在并递归调用equals()比较对应值。const keys Object.keys(a); if (keys.length ! Object.keys(b).length) return false; return keys.every(k equals(a[k], b[k]));完整实现与运行验证将上述五步组合起来就得到了一份完整的深度相等比较函数const equals (a, b) { if (a b) return true; if (a instanceof Date b instanceof Date) return a.getTime() b.getTime(); if (!a || !b || (typeof a ! object typeof b ! object)) return a b; if (a.prototype ! b.prototype) return false; const keys Object.keys(a); if (keys.length ! Object.keys(b).length) return false; return keys.every(k equals(a[k], b[k])); }; const a { name: John, age: 26 }; const b { name: John, age: 26 }; equals(a, b); // true内容相同 const c { name: John }; const d { name: John, age: undefined }; equals(c, d); // false键数量不同正确区分了 JSON.stringify 会误判的场景 const e [1, 2, 3]; const f { 0: 1, 1: 2, 2: 3 }; equals(e, f); // true数组与数字键对象在此实现下被视为相等从源码验证边界行为最后这个例子值得单独说明equals([1, 2, 3], { 0: 1, 1: 2, 2: 3 })返回true。从该实现看数组与数字索引对象拥有相同的键集合[0, 1, 2]且对应值相等Object.keys()无法区分二者因此被判定为相等。这正是从代码结构可以推断的行为特征该实现更关注键与值的结构一致性而非容器类型本身。如果你的业务要求严格区分数组与普通对象可以在递归前增加Array.isArray()的类型一致性检查。另一个值得留意的边界是原型链上的属性Object.keys()只返回对象自身的可枚举键因此该实现只比较自有属性继承属性不会参与比较。这与仓库中 object-has-value-or-key.md 提到的键检查只针对自身可枚举属性的约定一致。补充方案单向匹配与对称匹配深度相等是双向、全量的比较但很多业务场景只需要部分匹配例如表单校验、配置核对。仓库中的 match-object-properties.md 提供了另一组工具函数可作为深度相等的补充单向匹配判断第一个对象的键是否为第二个对象键的超集且对应值相等。只遍历source的键配合Object.prototype.hasOwnProperty()与const matches (obj, source) Object.keys(source).every( key obj.hasOwnProperty(key) obj[key] source[key] ); matches( { age: 25, hair: long, beard: true }, { hair: long, beard: true } ); // true对称匹配双向比较两个对象的键集合完全一致且值相等利用Set汇总双方键并校验集合大小const matchesSymmetric (a, b) { const keysA Object.keys(a), keysB Object.keys(b); const keys new Set([...Object.keys(a), ...Object.keys(b)]); if (keys.size ! keysA.length || keys.size ! keysB.length) return false; return [...keys].every( key a.hasOwnProperty(key) b.hasOwnProperty(key) a[key] b[key] ); }; matchesSymmetric( { hair: long, beard: true }, { age: 25, hair: long, beard: true } ); // false键集合大小不一致值得注意的是matches/matchesSymmetric默认用做值比较即浅层值比较而equals()用递归做深层值比较两者在嵌套对象场景下行为不同应根据数据深度选择。此外matchesWith/matchesSymmetricWith还支持传入自定义比较函数用于正则、忽略大小写等特殊匹配规则。关联比较与克隆、数组比较理解对象比较还需要把它放入整个js/comparison系列中看待。仓库的 comparison.yaml 将该主题组织为一条完整的知识链包含js/s/equality、js/s/object-comparison、js/s/array-comparison、js/s/date-comparison、js/s/object-is-triple-equals、js/s/match-object-properties、js/s/arrays-have-same-contents等条目。其中与对象比较最密切的两个关联主题是克隆对象按引用传递的机制决定了复制需要克隆。浅克隆只复制外层嵌套对象仍是引用深克隆递归或structuredClone()才能得到完全独立的副本。参见 shallow-deep-clone-object.md 与 deep-clone-structured-clone.md。需要注意的是克隆与比较是两个方向的问题——深度相等比较是判断两个对象内容是否一致而深克隆是生成一个内容一致但独立的对象二者常配合使用例如先克隆再比较或在不可变数据流中校验结果。数组比较数组是对象的一种特殊形态arrays-have-same-contents.md 讨论了忽略顺序、只看元素与频次的比较策略它使用Array.prototype.every()与Set统计唯一值思路与equals()的递归结构互相印证——都是先核对规模长度/键数量再逐项确认。总结与选型建议回顾三种方案可以给出如下的选型建议方案适用场景主要局限//Object.is()引用相等、原始值比较无法比较两个内容相同但引用不同的对象JSON.stringify()比较无undefined、无函数、无循环引用的扁平对象序列化会丢弃/改写特殊值键顺序影响结果递归深度相等equals()嵌套对象、Date、多层级结构的通用比较需要自行维护边界情况且对数组与数字键对象不区分实际工程中推荐优先采用递归深度相等这类显式实现或社区成熟的 deep-equal 类库并在其中显式处理业务相关的边界类型Date、Map、Set、RegExp、循环引用等。如果比较只是验证某个对象是否包含另一对象的属性子集matches系列是更轻量的选择。将 object-comparison.md 与js/comparison系列的其他文档结合阅读你就能系统掌握 JavaScript 值比较的全貌。赞分享教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载相关推荐30 Seconds of CodeJavaScript 中两个 Date 对象的比较相等、先后与区间判断完整指南30 Seconds of CodeJavaScript 中两个 Date 对象的比较相等、先后与区间判断完整指南 JavaScript 的 Date 对教程文档30 seconds of codeJavaScript 中 CSV 与数组、对象、JSON 的相互转换实战指南30 seconds of codeJavaScript 中 CSV 与数组、对象、JSON 的相互转换实战指南 CSV逗号分隔值 是一种以纯文本存储表格教程文档30 seconds of codeJavaScript 数组比较的完整实战指南30 seconds of codeJavaScript 数组比较的完整实战指南 导读 在 JavaScript 中比较两个数组是否相等是开发中最高频也教程文档上一篇pi-mono的可扩展性设计应对业务增长的架构考量下一篇ALVR有线串流实战指南突破Wi-Fi限制实现稳定低延迟VR体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

LKY_OfficeTools 一键安装 Office 指南:多语言、Office 2024 与独立卸载一次到位

LKY_OfficeTools 一键安装 Office 指南:多语言、Office 2024 与独立卸载一次到位

LKY_OfficeTools 一键安装 Office 指南:多语言、Office 2024 与独立卸载一次到位 【免费下载链接】LKY_OfficeTools 一键自动化 下载、安装、激活 Office 的利器。 项目地址: https://gitcode.com/GitHub_Trending/lk/LKY_OfficeTools LKY_OfficeTools 是一个…

2026/10/4 1:42:28 阅读更多 →
libmodbus 中的 modbus_get_float_abcd:从两个 Modbus 寄存器解析 ABCD 字节序浮点数

libmodbus 中的 modbus_get_float_abcd:从两个 Modbus 寄存器解析 ABCD 字节序浮点数

通信嵌入式物联网 【免费下载链接】libmodbus A Modbus library for Linux, Mac OS, FreeBSD and Windows 项目地址: https://gitcode.com/gh_mirrors/li/libmodbus 点击查看 免费下载 导读 modbus_get_float_abcd 是 libmodbus 提供的一组浮点工具函数之一&#…

2026/10/4 1:42:28 阅读更多 →
SS524V100 CVBS输出调试实战:从硬件准备到波形验证全流程

SS524V100 CVBS输出调试实战:从硬件准备到波形验证全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:42:28 阅读更多 →

最新新闻

零低速区脉振高频电压注入与中高速反电动势观测融合的永磁同步电机无位置传感器控制策略及参数鲁棒性研究(Simulink仿真实现)

零低速区脉振高频电压注入与中高速反电动势观测融合的永磁同步电机无位置传感器控制策略及参数鲁棒性研究(Simulink仿真实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 &#x1f381…

2026/10/4 2:11:51 阅读更多 →
es6基础知识

es6基础知识

16.你对面向对象编程的理解,面向过程和面向对象有什么区别? 对面向对象的理解:以对象为核心,靠封装、继承、多态组织代码,绑定数据与操作,强调对象交互和代码复用。 与面向过程的区别:面向对象以…

2026/10/4 2:11:51 阅读更多 →
VCS覆盖率实战:从line/toggle/fsm到covergroup建模

VCS覆盖率实战:从line/toggle/fsm到covergroup建模

1. 为什么覆盖率不是“跑完就完事”的数字游戏——从VCS仿真结果里挖出真实设计风险刚入行那会儿,我盯着VCS仿真结束时屏幕上跳出来的那行Coverage: 92.3%,心里还挺得意,觉得功能验证差不多能交差了。结果流片回来,一个看似冷门的…

2026/10/4 2:11:51 阅读更多 →
Skills不是工具而是能力契约:从零构建可跨IDE/CLI/Agent复用的技能模块

Skills不是工具而是能力契约:从零构建可跨IDE/CLI/Agent复用的技能模块

1. 这个“skills”到底是什么?别被热词带偏了方向最近在技术社区里,“skills”这个词像雨后春笋一样冒出来,和Claude、npx、agent、Code这些词高频捆绑出现。很多人一看到就下意识觉得这是某个新出的AI工具、某个神秘插件,或者干脆…

2026/10/4 2:11:50 阅读更多 →
上下文模式(Context-Mode):开发工具如何读懂你的操作意图

上下文模式(Context-Mode):开发工具如何读懂你的操作意图

写代码最怕的是什么?不是功能实现不了,而是工具明明装了一堆,关键时刻却"不听话"。你正盯着终端里几十行编译报错,它弹出的提示跟当前语境毫无关系;你刚打开一个Python文件,代码补全还停留在上个…

2026/10/4 2:11:50 阅读更多 →
工业机器人视觉引导:手眼标定与目标位姿估计实战

工业机器人视觉引导:手眼标定与目标位姿估计实战

简介:这份PDF面向工业机器人视觉引导方向的工程师、研究生与自动化从业者,围绕OpenCV展开手眼标定与目标位姿估计的完整技术链路,帮助读者解决从相机标定到精准抓取路径设计的实际工程问题。文档共428页、51个大章节,支持目录跳转…

2026/10/4 2:10:50 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →