如何实现深拷贝?structuredClone

news/2024/9/19 12:36:17/文章来源:https://www.cnblogs.com/miniwa/p/18405777

经典的面试题:如何实现深拷贝。

常规的回答主要是通过JSON或者遍历对象递归。主要是考核对对象操作方法的熟悉程度。今天来介绍另一个方案structuredClone()

什么是 structuredClone()

structuredClone() 是在 2022 年引入的一个全局函数,它使得 JavaScript 对象的深度克隆变得可能。与传统的方法如 JSON.stringify()JSON.parse() 不同,这些方法在处理复杂结构和循环引用时会遇到困难,structuredClone() 却能轻松应对这些挑战。

基本用法

它是一个强大的工具,用于创建真正的深度克隆,保持嵌套对象和循环引用的完整性,无需额外的逻辑或变通方法。此外,它在现代环境中可用,包括 Web Workers。

1. 简单对象克隆:基础

使用 {...obj}(浅拷贝)

const original = {name: "Alice",details: { age: 25 }
};const shallowCopy = { ...original };shallowCopy.details.age = 30;console.log(original.details.age); // 30
console.log(shallowCopy.details.age); // 30

发生了什么?

展开运算符 {...obj} 仅创建浅拷贝。details 对象没有被深度克隆,因此对 shallowCopy.details 的更改也会影响原始的 details

使用 JSON.stringify() + JSON.parse()(深拷贝)

const original = {name: "Alice",details: { age: 25 }
};const deepCopy = JSON.parse(JSON.stringify(original));deepCopy.details.age = 30;console.log(original.details.age); // 25
console.log(deepCopy.details.age); // 30

这种方法创建了一个深拷贝,但它有局限性:它无法处理函数、undefined 或循环引用。

使用 structuredClone()(深拷贝)

const original = {name: "Alice",details: { age: 25 }
};const clone = structuredClone(original);clone.details.age = 30;console.log(original.details.age); // 25
console.log(clone.details.age); // 30

structuredClone() 创建了一个深拷贝,保留了结构,没有任何 JSON.stringify() 的限制,并能处理循环引用和 undefined 等复杂数据类型。

2. 循环引用

使用 {...obj} 处理循环引用

const original = {name: "Alice",self: null
};original.self = original; // 这将导致错误:const shallowCopy = { ...original };// TypeError: Converting circular structure to JSON

{...obj} 无法处理循环引用,结果会导致错误。

使用 JSON.stringify() 处理循环引用

const original = {name: "Alice",self: null
};original.self = original; // 这将导致错误:const jsonCopy = JSON.parse(JSON.stringify(original));// TypeError: Converting circular structure to JSON

JSON.stringify() 也无法处理循环引用,会抛出错误。

使用 structuredClone() 处理循环引用

const original = {name: "Alice",self: null
};original.self = original;
const clone = structuredClone(original);console.log(clone !== original); // true
console.log(clone.self === clone); // true

structuredClone() 轻松处理循环引用,创建了正确的深度克隆,没有错误。

3. 克隆函数和 undefined

使用 {...obj}

const original = {name: "Alice",greet: () => "Hello!",value: undefined
};const shallowCopy = { ...original };console.log(shallowCopy.greet()); // "Hello!"
console.log(shallowCopy.value); // undefined

发生了什么?

{...obj} 如预期那样拷贝函数和 undefined,但仅是浅拷贝。

使用 JSON.stringify()

const original = {name: "Alice",greet: () => "Hello!",value: undefined
};const jsonCopy = JSON.parse(JSON.stringify(original));console.log(jsonCopy.greet); // undefined
console.log(jsonCopy.value); // undefined

JSON.stringify() 无法序列化函数或 undefined,导致它们在克隆对象中丢失。

使用 structuredClone()

const original = {name: "Alice",greet: () => "Hello!",value: undefined
};const clone = structuredClone(original);console.log(clone.greet); // undefined
console.log(clone.value); // undefined

structuredClone() 也不克隆函数,但保留了 undefined 值,使其比 JSON.stringify() 更可靠,适用于复杂对象。

4. 速度和效率:性能说明

处理大数据的效率

const largeArray = new Array(1e6).fill({ key: "value" });console.time("structuredClone");
const clone = structuredClone(largeArray);
console.timeEnd("structuredClone");console.time("JSON.stringify + JSON.parse");
const jsonCopy = JSON.parse(JSON.stringify(largeArray));
console.timeEnd("JSON.stringify + JSON.parse");

对于大型、复杂的数据,structuredClone() 通常比 JSON.stringify() + JSON.parse() 更快,并且避免了序列化和反序列化的问题。

再深入了解一下

全局的 structuredClone() 方法使用结构化克隆算法将给定的值进行深拷贝。

该方法还支持把原值中的可转移对象转移(而不是拷贝)到新对象上。可转移对象与原始对象分离并附加到新对象;它们将无法在原始对象中被访问。

structuredClone(value, { transfer })

value

被克隆的对象。可以是任何结构化克隆支持的类型。

JavaScript 类型

  • Array
  • ArrayBuffer
  • Boolean
  • DataView
  • Date
  • Error 类型(仅限部分 Error 类型)。
  • Map
  • Object 对象:仅限简单对象(如使用对象字面量创建的)。
  • 除 symbol 以外的基本类型。
  • RegExp:lastIndex 字段不会被保留。
  • Set
  • String
  • TypedArray

Error 类型

  • 仅支持以下 Error 类型:Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError(或其他会被设置为 Error 的)。
  • 浏览器必须序列化 name 和 message 字段,其他有意义的字段则可能会序列化,如 stack、cause 等。

transfer 可选

是一个可转移对象的数组,里面的 值 并没有被克隆,而是被转移到被拷贝对象上。

一个可能有用的场景是在保存 buffer 之前先异步的校验里面的数据。为了避免 buffer 在保存之前有其他修改,你可以先克隆这个 buffer 然后校验数据。为了防止意外的错误引用,在传输数据时,任何修改 buffer 的尝试都会失败。

var uInt8Array = new Uint8Array(1024 * 1024 * 16); // 16MB
for (var i = 0; i < uInt8Array.length; ++i) {uInt8Array[i] = i;
}const transferred = structuredClone(uInt8Array, {transfer: [uInt8Array.buffer],
});
console.log(uInt8Array.byteLength); // 0

结构化克隆算法用于复制复杂 JavaScript 对象的算法。常用于浏览器内部的数据传递,例如 Worker 的 postMessage() 或使用 IndexedDB 存储对象用。它通过递归输入对象来构建克隆,同时保持先前访问过的引用的映射,以避免无限遍历循环。

结构化克隆所不能做到的

  • Function 对象是不能被结构化克隆算法复制的;如果你尝试这样子去做,这会导致抛出 DATA_CLONE_ERR 的异常。
  • 企图去克隆 DOM 节点同样会抛出 DATA_CLONE_ERR 异常。
  • 对象的某些特定参数也不会被保留
    • RegExp 对象的 lastIndex 字段不会被保留
    • 属性描述符,setters 以及 getters(以及其他类似元数据的功能)同样不会被复制。例如,如果一个对象用属性描述符标记为 read-only,它将会被复制为 read-write,因为这是默认的情况下。
    • 原形链上的属性也不会被追踪以及复制。

总结

structuredClone()的优势如下:

  • 可靠性:更可预测地处理循环引用、函数和 undefined 值。

  • 效率:对于大型数据集执行深度克隆更快,不需要变通方法。

  • 简洁性:一种方法统治它们所有 —— 不再需要在 {...obj}JSON.stringify() 或自定义深度克隆函数之间选择。

本文由mdnice多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/794811.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信奥一本通题陈老师解题:1209:分数求和

​ 【题目描述】【输入】第一行是一个整数nn,表示分数个数,1≤n≤101≤n≤10; 接下来nn行,每行一个分数,用"p/qp/q"的形式表示,不含空格,p,qp,q均不超过1010。【输出】输出只有一行,即最终结果的最简形式。若为分数,用"p/qp/q"的形式表示。【输…

【OpenFeign 】OpenFeign 下的重试器的执行过程分析

1 前言 上节我们看了下 OpenFeign 里的重试,在从源码的角度看它的执行原理的时候,又意外的遇到了一个【OpenFeign 】OpenFeign 下未开启重试,服务却被调用了两次 的问题的分析,那本节我们就来看看重试器的一个入场以及执行的过程。 2 源码分析 首先我们要知道在默认的情况…

WiFi基础(三):802.11ac/ax/be 与 WiFi4、WiFi5、WiFi6、WiFi7

前面我们介绍了 802.11 b/g/n 的一些核心技术和基础概念,本章将介绍目前比较新的 WiFi5 和 WiFi6,以及在今年会发布的 WiFi7。liwen01 2024.09.08 前言 经过二十多年的发展,WiFi 在硬件能力、软件和算法、频谱资源、市场需求、电源与能效方面都有了很大的提升。所以我们能看…

一个类才几百行/搞定各种自定义委托/涵盖各种场景需求/所有委托一网打尽/用法极其简单

一、应用场景某个字段需要提供下拉框进行选择,下拉框可选是否允许编辑。 某个字段需要提供密码框进行输入,密文显示字段值。 某个字段需要提供日期框下拉选择日期时间。 某个字段需要提供微调框设定值。 某个字段需要提供进度条显示字段值。 某个字段列需要禁用。 各种委托控…

军工厂电气工程师到知名互联网公司程序员,我吃饭的家伙有哪些

大家好,我是欧阳。今年刚好是欧阳三十而立之年,虽然没有立起来。这篇文章来聊聊我从一名军工厂电气工程师到某知名互联网公司程序员,这期间我吃饭的家伙都有哪些。 军工厂期间 欧阳大学读的是“电气工程及其自动化专业”,毕业后进入了老家的一个军工厂,成为了一名电气工程…

用 SQL 写的俄罗斯方块游戏「GitHub 热点速览」

在开始介绍上周热门开源项目之前,要插播一条开源新闻:Nginx 已正式迁移至 GitHub。 近日,Nginx 官方宣布将 Nginx 开源项目,从 Mercurial 迁移至 GitHub 代码托管平台,并开始接受 PR 形式的贡献、Issues 问题反馈和功能请求等,GitHub 上的 Nginx 项目终于“活”了!GitHu…

喜欢干净简洁音乐播放器的朋友看过来

大家好,我是晓凡。 不少程序员小伙伴都喜欢边听音乐边敲代码,尤其在一个嘈杂的环境中,一个好的想法、好的思路可能就因为一瞬间的干扰就没了。 这时,如果耳机一戴上,听着音乐能更好的集中注意力;遇到bug也能临危不乱,想出更好的解决办法; 网易云音乐,算是一个相对简洁…

在vue3中手写按需加载图片

在我们的网页中.假如使用了大量的图片,每个图片都是需要去访问加载的 这就影响了我们的访问速度,手写一个按需加载组件,就可以解决这个问题 让图片处于页面视图的时候再加载,减轻网页访问负担利用vue3官网给出的钩子 我们常用的就是onMountent 如官网所示为了及时监测,这里使用…

单个48TB大小SQL Server数据库备份导致日志文件无法截断

单个48TB大小SQL Server数据库备份导致日志文件无法截断SQL Server 版本:SQL Server 2019背景在一个48T大小的单数据库环境中,采用简单恢复模式,日志文件大小限制为600G。执行一次完整备份时,耗时超过12小时,导致日志文件无法截断并达到上限,后续事务无法正常写入,导致整…

第一章 联言命题选言命题及其推理-德摩根定律及其练习题

听他讲一遍怎么做 自己怎么做 ==》对比 真值表做题!

第一章 联言命题选言命题及其推理-选言命题性质

可以同时发生的 相容选言命题 具备并存关系

联言命题选言命题及其推理-选言命题性质

可以同时发生的 相容选言命题 具备并存关系