使用结构化克隆在 JavaScript 中进行深度复制

简介: 使用结构化克隆在 JavaScript 中进行深度复制

在很长一段时间内,您不得不求助于变通方法和库来创建 JavaScript 值的深层副本。现在js提供 「structuredClone()」 一个用于深度复制的内置函数。

浏览器支持:



image.png

浅拷贝



在 JavaScript 中复制一个值几乎是浅拷贝,而不是深拷贝。这意味着对深度嵌套值的更改将在副本和原始值中可见。 使用对象扩展运算符 在 JavaScript 中创建浅拷贝的一种方法


const myOriginal = {
  someProp: "with a string value",
  anotherProp: {
    withAnotherProp: 1,
    andAnotherProp: true
  }
};
const myShallowCopy = {...myOriginal};


直接在浅副本上添加或更改属性只会影响副本,而不影响原始值:


myShallowCopy.aNewProp = "a new value";
console.log(myOriginal.aNewProp)
// ^ logs `undefined`


然而,添加或更改嵌套很深的属性会影响原始值和拷贝值:


myShallowCopy.anotherProp.aNewProp = "a new value";
console.log(myOriginal.anotherProp.aNewProp) 
// ^ logs `a new value`


对象扩展运算符遍历了myOriginal所有可枚举的属性。它使用属性名称和值,并将它们一一分配给一个新创建的空对象。因此,生成的对象在形状上是相同的,但具有自己的属性和值列表的副本。这些值也会被复制,但是 JavaScript 值处理所谓的基本类型的方式与处理非基本类型的方式不同。


非基本类型作为引用处理,这意味着复制值的行为实际上只是复制对同一底层对象的引用,从而导致浅复制行为。


深拷贝



与浅拷贝相反的是深拷贝。深拷贝算法也一个一个地拷贝一个对象的属性,但是当它找到对另一个对象的引用时递归地调用自己,同时创建该对象的副本。这对于确保两段代码不会意外共享一个对象并在不知不觉中操纵彼此的状态非常重要。


在 JavaScript 中创建值的深层副本过去没有简单或好的方法。很多人依赖第三方库,比如Lodash 的cloneDeep()函数。可以说,这个问题最常见的解决方案是基于 JSON 的 hack:


const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));


事实上,这是一个非常流行的解决方法,V8 积极优化 JSON.parse(),特别是上面的模式,以使其尽可能快。虽然速度很快,但它也有一些缺点:


  • 递归数据结构:JSON.stringify()当你序列化一个递归数据结构时会报错。在使用链表或树时,这很容易发生。
  • 内置类型:JSON.stringify()如果值包含其他JS关键字,例如Map,Set,Date,RegExp或ArrayBuffer,也会报错。
  • Functions:JSON.stringify()有可能会丢掉函数。


结构化克隆



浏览器已经需要在几个地方创建 JavaScript 值的深层副本的能力:在 IndexedDB 中存储 JS 值需要某种形式的序列化,以便可以将其存储在磁盘上,然后反序列化以恢复 JS 值。类似地,向 WebWorker 发送消息postMessage()需要将 JS 值从一个 JS 领域传输到另一个领域。


HTML 规范进行了修改,以公开一个名为的函数structuredClone(),该函数完全运行该算法,作为开发人员轻松创建 JavaScript 值的深层副本的一种手段。


const myDeepCopy = structuredClone(myOriginal);


特点和限制



结构化克隆解决了该JSON.stringify()技术的许多(尽管不是全部)缺点。结构化克隆可以处理循环数据结构,支持许多内置数据类型,并且通常更健壮且通常更快。


但是,它仍然有一些限制可能会让您措手不及:


  • Prototypes:如果structuredClone()与类实例一起使用,您将获得一个普通对象作为返回值,因为结构化克隆会丢弃对象的原型链。
  • 函数:同样不支持函数。
  • Non-cloneables:一些值不是结构化可克隆的,最值得注意的是Error和 DOM 节点。它会导致structuredClone()抛出异常。


如果这些限制中的任何一个对您的用例造成破坏,像 Lodash 这样的库仍然提供其他深度克隆算法的自定义实现,这些算法可能适合您的用例,也可能不适合您的用例。

相关文章
|
JavaScript 前端开发
你真的能区分JavaScript的各种导入导出方式吗?
你真的能区分JavaScript的各种导入导出方式吗?
67 0
|
存储 自然语言处理 JavaScript
JavaScript基础系列(6):`this`这六种使用方式,你都理解了吗?
执行发现两个this都指向了全局的window.也就是全局执行上下文中的this和函数执行上下文中的this都指向了全局window.所以默认的情况下,在全局中调用一个函数,函数中执行上下文中的this都是指向window对象的。
119 0
|
移动开发 资源调度 JavaScript
12 个你可能从未使用过的有用的 JavaScript 库
JavaScript 在不断发展,几乎每天都有新库出来,或者扩展旧库以支持新功能。因此,我们现在拥有数千个 JavaScript 库。并非所有这些都对一般 Web 开发人员有用。
12 个你可能从未使用过的有用的 JavaScript 库
|
JavaScript 前端开发
使用ABAP和JavaScript代码生成PDF文件的几种方式
使用ABAP和JavaScript代码生成PDF文件的几种方式
使用ABAP和JavaScript代码生成PDF文件的几种方式
|
移动开发 JavaScript 前端开发
开发者必备的 12 个 JavaScript 库
现在 web 设计是最有趣的了,做好 web 设计不仅要熟练使用 Javascript,css 和 html 等,还要有自己的创意设计。为了方便大家发挥自己的 创意,就产生了很多 JS 框架,Node.js 扩展等等。有了这些工具,开发者们就能专注于创意设计了,而不用为某个功能而 花费太多精力。这里我们介绍的是 12 个开发者们必备的 JavaScript 库 ,都是一些很基础功能很强大的库。有了这些库,开发者们可以节省很多时间,大 大提高开发的效率,所以大家赶紧收藏起来吧:)
233 0
开发者必备的 12 个 JavaScript 库
|
存储 缓存 JavaScript
写一个更好的 Javascript DOM 库
目前,jQuery是事实上的操作文档对象模型(DOM)的库。它可以与流行的客户端MV*框架结合使用,并且拥有大量的插件与大型的社区。开发者对于Javascript的兴趣与日俱增的同时,很多人开始好奇,原生的API是如何工作的,以及我们何时应该直接使用它们而不是引用一个额外的库。
126 0
写一个更好的 Javascript DOM 库
|
JavaScript 前端开发 IDE
MDJS:可将 JavaScript 添加到 Markdown,创建交互式文档
Open Web Components( @OpenWc )创建者 Thomas Allmer 发布了 MDJS ,这是一种 Markdown 变体,支持在 Markdown 文档中包含可运行的 JavaScript 代码。
|
前端开发 JavaScript
javaScript进阶-javaScript数据转换
(一) Number-->String var str=String(num) var str=num.ToString(); 有一次在开发中使用 str.length,str是有值的,但是str.
673 0
|
Web App开发 JavaScript 前端开发
2018年你需要知道的13个JavaScript工具库
v2-307971e2f3800cf8ee6fde8a2e9ea1cc_1200x500.jpg 译者按: 你可能已经用到Underscore或者Lodash。
1383 0
|
XML 前端开发 JavaScript