ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JavaScript 深拷贝完全指南:structuredClone() 与其他克隆方法的全面对比

JavaScript 深拷贝完全指南:structuredClone() 与其他克隆方法的全面对比 教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载JavaScript 中的对象克隆是极易踩坑的场景浅拷贝会在嵌套数据上漏馅JSON.stringify()会丢弃函数与特殊类型手写递归深拷贝也可能遗漏细节。本文围绕 30-seconds-of-code 仓库中的同名技术文章展开系统讲解structuredClone()的原理、边界行为与适用场景并通过逐项对比 shallowClone、deepClone、JSON 序列化三种方案帮助读者在真实项目中做出正确的克隆选型。说明下文引用的示例代码与对比结论均来自 deep-clone-structured-clone.md 原文仓库相关路径均已转换为以仓库根目录为起点的相对链接。一、克隆方法总览四种方案的定位差异在深入structuredClone()之前先看四种常见克隆方案的核心实现与定位差异。以下是原文给出的四种典型实现const shallowClone obj ({ ...obj }); const deepClone obj { if (obj null) return null; let clone Object.assign({}, obj); Object.keys(clone).forEach( key (clone[key] typeof obj[key] object ? deepClone(obj[key]) : obj[key]) ); if (Array.isArray(obj)) { clone.length obj.length; return Array.from(clone); } return clone; }; const jsonClone obj JSON.parse(JSON.stringify(obj));四种方案的定位差异如下shallowClone使用展开运算符创建对象的浅拷贝嵌套对象仍是对原对象的引用deepClone递归克隆对象能处理嵌套对象与数组但对特殊类型无能为力jsonClone借助JSON.stringify()与JSON.parse()实现深拷贝但受限于 JSON 数据类型structuredClone()JavaScript 原生内置方法基于 结构化克隆算法能处理其他方案无法覆盖的众多边界情况。[!NOTE] 关于克隆方法的详细讲解可参见仓库内的前序文章 shallow-deep-clone-object.md该文解释了为什么对象按引用传递、浅克隆与深克隆的区别并指出structuredClone()自 Node.js v17.0.0 起可用。二、对比总表逐项能力一览原文给出了四方案在九大特性上的完整对比表这是理解structuredClone()价值与局限的核心依据FeatureshallowClonedeepCloneJSONstructuredCloneNested objects/arrays⚠️✅✅✅Functions⚠️⚠️❌❌ ErrorBuilt-in types⚠️❌[1]❌✅DOM nodes⚠️❌[2]❌❌ ErrorCircular references⚠️❌[3]❌ Error✅Prototype chain❌❌[4]❌❌Getters/Setters❌❌[5]❌❌Symbol properties✅✅❌❌Private fields❌❌❌❌图例✅ 支持❌ 不支持⚠️ 引用浅拷贝Error 抛出异常 [1] 内置类型通常无法被正确克隆除非专门处理 [2] 克隆 DOM 节点行为不一致且因实现而异 [3] 循环引用会抛出异常除非专门处理 [4] 原型链能否保留取决于具体实现 [5] 属性描述符可以保留若显式处理从上表可以清晰看到structuredClone()是唯一能同时正确处理嵌套数据、内置类型与循环引用的内置方案但它同样不保留原型链、访问器、Symbol 属性与私有字段。下文逐项展开这些边界情况。三、逐项边界情况剖析以下逐一分析各特殊场景下structuredClone()与其他方案的行为差异嵌套对象与数组除外因为除浅拷贝外其余方案行为一致。3.1 函数Functions不支持克隆structuredClone()与JSON.stringify()都不会克隆函数。浅拷贝与自定义深拷贝仅复制函数引用而不克隆函数本身——这意味着克隆对象仍引用原函数在配合闭包或this上下文时可能引发意外行为。const obj { fn: () 1 }; shallowClone(obj).fn obj.fn; // true deepClone(obj).fn obj.fn; // true jsonClone(obj).fn; // null structuredClone(obj); // throws DataCloneError相关主题可参考 closures.md。3.2 内置类型Built-in typesstructuredClone 的强项Date、Map、Set、RegExp等内置对象无法被JSON.stringify()正确克隆——它们会被转成字符串或空对象。浅拷贝复制引用自定义深拷贝除非专门处理否则也会失败。而structuredClone()能正确处理这些类型在克隆时保留其结构与属性。[!WARNING] 结构化克隆算法对Error类型对象的处理略有不同可查阅 MDN 官方文档 了解详情。const obj { date: new Date(2025-06-07), map: new Map([[key, value]]), set: new Set([1, 2, 3]), regexp: /abc/g, }; shallowClone(obj).date obj.date; // true (Date object, same for others) deepClone(obj).date; // Empty object (same for others) jsonClone(obj).date; // String representation (the rest are empty objects) structuredClone(obj).date obj.date; // true (Date object, same for others)[!IMPORTANT]几乎所有原生类型都有基于构造函数的直截了当的克隆方案。如果无法使用structuredClone()可以在自定义深拷贝函数中为它们添加专门的处理逻辑。3.3 DOM 节点DOM nodes明确抛错structuredClone()无法克隆 DOM 节点尝试克隆会抛出DataCloneError。这符合直觉——克隆 DOM 节点意味着要在文档中创建新节点。其他方案虽然不会抛错但处理得同样不优雅返回空对象或空节点静默失败可能反而更糟相关讨论见 vocal-fails-silencing-errors.md。const obj { node: document.querySelector(body) }; shallowClone(obj).node obj.node; // true deepClone(obj).node; // Empty DOM node jsonClone(obj).node; // Empty object structuredClone(obj); // throws DataCloneError3.4 循环引用Circular referencesstructuredClone 的招牌能力structuredClone()开箱即用地支持循环引用。JSON.stringify()会抛出异常大多数自定义深拷贝函数若不专门设计也会失败。const obj {}; obj.self obj; shallowClone(obj).self obj; // true deepClone(obj).self; // throws RangeError: Maximum call stack size exceeded JSON.stringify(obj); // throws TypeError: Converting circular structure to JSON structuredClone(obj).self obj; // false对于必须用 JSON 序列化循环引用的场景可参考仓库文章 stringify-circular-json.md——它用WeakSet追踪已访问对象、配合自定义replacer过滤循环引用从而避免TypeError代价是序列化结果会丢失循环数据。3.5 原型链Prototype chain最令人意外的短板structuredClone()最令人意外的一点是它不保留原型链。无论原对象的原型是什么克隆对象的原型始终是Object.prototype。不过其他三种方案同样不保留原型链除非自定义深拷贝函数显式处理。const proto { x: 1 }; const obj Object.create(proto); shallowClone(obj).x; // undefined deepClone(obj).x; // undefined jsonClone(obj).x; // undefined structuredClone(obj).x; // undefined原型与继承的相关背景可参考 classical-vs-prototypal-inheritance.md。3.6 存取器Getters/Setters只克隆当前值所有方案包括structuredClone()都不保留 getter/setter只克隆当前值。克隆对象不再具备原对象的访问器行为。概括来说任何方案都不会保留属性描述符等元数据类特性除非在自定义深拷贝函数中显式处理。const obj { valueField: 42, get value() { return this.valueField; }, set value(val) { this.valueField val; }, }; typeof obj.value; // object (getter/setter) typeof shallowClone(obj).value; // number (independent values) typeof deepClone(obj).value; // number (independent values) typeof jsonClone(obj).value; // number (independent values) typeof structuredClone(obj).value; // number (independent values)若需要精确控制属性描述符可参考 add-key-value-pair-to-object.md 中关于Object.defineProperty()的用法。3.7 Symbol 属性Symbol properties被忽略structuredClone()不克隆以 Symbol 为键的属性而是像JSON.stringify()一样直接忽略。浅拷贝与自定义深拷贝则按引用复制 Symbol 属性。const sym Symbol(key); const obj { [sym]: value }; shallowClone(obj)[sym] obj[sym]; // true deepClone(obj)[sym] obj[sym]; // true jsonClone(obj)[sym]; // undefined structuredClone(obj)[sym]; // undefined3.8 私有字段Private fields同样不克隆私有类字段与方法同样不会被structuredClone()克隆。克隆结果只包含公开、可枚举的属性。这与其余方案一致——私有属性不属于对象的可枚举属性因此都无法被复制。class Secret { #hidden 42; value 7; get hidden() { return this.#hidden; } }; const obj new Secret(); shallowClone(obj); // #hidden is not cloned deepClone(obj); // #hidden is not cloned jsonClone(obj); // #hidden is not cloned structuredClone(obj); // #hidden is not cloned四、性能考量structuredClone()是内置方法通常针对性能做过优化。尤其在处理含循环引用或内置类型的复杂对象时它快于自定义深拷贝函数和JSON.stringify()。一个实用的经验法则只要structuredClone()能满足你的使用场景它的性能大概率优于任何手写方案。再加上随着 JavaScript 引擎不断优化其性能还有持续提升的空间——这使它成为深拷贝的可靠选择。五、何时使用 structuredClone()structuredClone()是原生、无负担地深克隆 JavaScript 对象的强大工具。只要不存在自定义深拷贝才能处理的特殊边界需求都应优先选用structuredClone()——它快速、安全并能处理诸多会击穿其他方案的边界情况。以下场景可以考虑改用自定义深拷贝函数需要保留原对象的原型链需要克隆函数或 DOM 节点需要保留属性描述符、getter 或 setter需要保留 Symbol 属性。即便如此也常常可以围绕structuredClone()编写一个薄封装在保留其性能与可靠性的同时只为所需场景补充额外处理逻辑。[!WARNING] 结构化克隆算法可能在本文未覆盖的场景抛出DataCloneError尤其是平台特定对象或类型。务必针对自己的用例进行测试。支持类型的完整列表可查阅 MDN 关于 Structured clone algorithm 的页面。六、结语structuredClone()是 JavaScript 中强大而通用的深拷贝方法。它解决了其他方案难以处理的诸多边界问题——循环引用、内置类型、复杂数据结构等。尽管存在不保留原型链、无法克隆函数等局限它依然是绝大多数日常场景的最佳选择。进一步阅读围绕克隆与对象操作的更多实战文章可直接在仓库中继续阅读shallow-deep-clone-object.md从按引用传递到深浅克隆的完整讲解structuredClone()的入门前置知识copy-array.md数组克隆的多种方式汇总含structuredClone()对数组的深拷贝用法stringify-circular-json.md用WeakSet处理循环引用的 JSON 序列化技巧vocal-fails-silencing-errors.md为何响亮地失败优于静默地失败与DataCloneError抛错的价值相呼应。此外仓库的 javascript.yaml 语言资源页中收录了structuredClone()的 MDN 文档链接可作为进一步查阅的入口。赞分享教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载相关推荐30 Seconds of Interviews 面试精讲JavaScript 对象克隆浅拷贝与深拷贝的完整方案30 Seconds of Interviews 面试精讲JavaScript 对象克隆浅拷贝与深拷贝的完整方案 导读 本篇文章围绕 30 Seconds教程前端CANN asc-devkit SIMD-API C-API asc_int82int32函数asc_int82int32 废弃 产品支持情况 | 产品 | 是否支持 | | | : : | | Ascend 950PR/Ascend 950DT | √人工智能深度学习算子库CANNAscendNetdata Windows 监控静默安装 5 分钟从服务验证到告警闭环Netdata Windows 监控静默安装 5 分钟从服务验证到告警闭环 本文面向维护混合环境的系统管理员与 DevOps 工程师用 Netdata W可观测性指标监控告警异常检测上一篇为什么DNSHE免费DNS解析这么快Anycast DNS技术原理深度剖析下一篇终极指南揭秘DINO注意力机制中qk_scale缩放因子的核心作用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表