JavaScript中现代深度克隆方法

知道吗,在JavaScript中现在有一种原生的方法来做对象的深层复制?

没错,这个 structuredClone 函数内置在JavaScript运行时中:

js 复制代码
const calendarEvent = {
  title: "Builder.io Conf",
  date: new Date(123),
  attendees: ["Steve"]
}

const copied = structuredClone(calendarEvent)

所有的结果都如预期的那样:

js 复制代码
copied.attendees // ["Steve"]
copied.date // Date: Wed Dec 31 1969 16:00:00
cocalendarEvent.attendees === copied.attendees // false

是的, structuredClone 不仅可以做以上的事情,而且还可以:

  • 克隆无限嵌套的对象和数组
  • 克隆循环引用
  • 克隆各种各样的JavaScript类型,如 DateSetMapErrorRegExpArrayBufferBlobFileImageData
  • 转让任何可转移对象

例如:

js 复制代码
const kitchenSink = {
  set: new Set([1, 3, 3]),
  map: new Map([[1, 2]]),
  regex: /foo/,
  deep: { array: [ new File(someBlobData, 'file.txt') ] },
  error: new Error('Hello!')
}
kitchenSink.circular = kitchenSink

const clonedSink = structuredClone(kitchenSink)

值得注意的是,我们谈论的是一个深度副本。如果你只需要做一个浅拷贝,也就是不复制嵌套对象或数组的拷贝,那么我们可以只做一个对象扩展:

js 复制代码
const simpleEvent = {
  title: "Builder.io Conf",
}

const shallowCopy = {...calendarEvent}

如果你喜欢的话,也可以是这个

js 复制代码
const shallowCopy = Object.assign({}, simpleEvent)
const shallowCopy = Object.create(simpleEvent)

但是一旦我们有了嵌套项,我们就会遇到麻烦:

js 复制代码
const calendarEvent = {
  title: "Builder.io Conf",
  date: new Date(123),
  attendees: ["Steve"]
}

const shallowCopy = {...calendarEvent}

shallowCopy.attendees.push("Bob")

shallowCopy.date.setTime(456)

正如你所看到的,我们没有做这个对象的完整副本。

为什么不是 JSON.parse(JSON.stringify(x))

举个例子:

js 复制代码
const calendarEvent = {
  title: "Builder.io Conf",
  date: new Date(123),
  attendees: ["Steve"]
}

const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))

如果我们打印 problematicCopy ,我们将得到:

js 复制代码
{
  title: "Builder.io Conf",
  date: "1970-01-01T00:00:00.123Z"
  attendees: ["Steve"]
}

这不是我们想要的! date 应该是 Date 对象,而不是字符串。

这是因为 JSON.stringify 只能处理基本的对象、数组和原语。任何其他类型都难以预测。例如,日期转换为字符串。Set 只是转换为 {}

JSON.stringify 甚至完全忽略了某些东西,比如 undefined 或函数。

例如,如果我们复制 kitchenSink

js 复制代码
const kitchenSink = {
  set: new Set([1, 3, 3]),
  map: new Map([[1, 2]]),
  regex: /foo/,
  deep: { array: [ new File(someBlobData, 'file.txt') ] },
  error: new Error('Hello!')
}

const veryProblematicCopy = JSON.parse(JSON.stringify(kitchenSink))

我们将得到:

js 复制代码
{
  "set": {},
  "map": {},
  "regex": {},
  "deep": {
    "array": [
      {}
    ]
  },
  "error": {},
}

_.cloneDeep

到目前为止,Lodash的 cloneDeep 函数一直是这个问题的一个非常常见的解决方案。

事实上,这是按照预期工作的:

js 复制代码
import cloneDeep from 'lodash/cloneDeep'

const calendarEvent = {
  title: "Builder.io Conf",
  date: new Date(123),
  attendees: ["Steve"]
}

const clonedEvent = cloneDeep(calendarEvent)

structuredClone 什么不能克隆

不能克隆函数

会抛出一个 DataCloneError 异常:

js 复制代码
// 🚩 Error!
structuredClone({ fn: () => { } })

DOM节点

也会抛出 DataCloneError 异常:

js 复制代码
// 🚩 Error!
structuredClone({ el: document.body })

属性描述符、setter和getter

以及类似元数据的功能不会被克隆。

例如,使用getter,结果值被克隆,但getter函数本身(或任何其他属性元数据)不会被克隆:

js 复制代码
structuredClone({ get foo() { return 'bar' } })
// Becomes: { foo: 'bar' }

对象原型

原型链不会被遍历或复制。因此,如果您克隆 MyClass 的实例,则克隆的对象将不再是该类的实例(但该类的所有有效属性将被克隆)。

js 复制代码
class MyClass { 
  foo = 'bar' 
  myMethod() { /* ... */ }
}
const myClass = new MyClass()

const cloned = structuredClone(myClass)
// Becomes: { foo: 'bar' }

cloned instanceof myClass // false

支持类型的完整列表

更简单地说,任何不在下面列表中的内容都不能被克隆:

js 内置

ArrayArrayBufferBooleanDataViewDateError 类型(下面特别列出的)、 MapObject ,但仅限于普通对象(例如,从对象字面量),原始类型,除了 symbol (又名 numberstringnullundefinedbooleanBigInt ), RegExpSetTypedArray

错误类型

Error, EvalError, RangeError, ReferenceError , SyntaxError, TypeError, URIError

WEB/API 类型

AudioData, Blob, CryptoKey, DOMException, DOMMatrix, DOMMatrixReadOnly, DOMPoint, DomQuad, DomRect, File, FileList, FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemHandle, ImageBitmap, ImageData, RTCCertificate, VideoFrame

原文: www.builder.io/blog/struct...

相关推荐
典学长编程2 分钟前
前端开发(HTML,CSS,VUE,JS)从入门到精通!第三天(JavaScript)
前端·javascript·css·html·前端开发
寻月隐君6 分钟前
Rust Scoped Threads 实战:更安全、更简洁的并发编程
后端·rust·github
德育处主任8 分钟前
p5.js 用 beginGeometry () 和 endGeometry () 打造自定义 3D 模型
前端·前端框架·canvas
文火冰糖的硅基工坊19 分钟前
[硬件电路-124]:模拟电路 - 信号处理电路 - 测量系统的前端电路详解
前端·嵌入式硬件·fpga开发·架构·信号处理·电路
爷_1 小时前
手把手教程:用腾讯云新平台搞定专属开发环境,永久免费薅羊毛!
前端·后端·架构
山间小僧1 小时前
「查漏补缺」ZGC相关内容整理
java·jvm·后端
狂炫一碗大米饭1 小时前
如何在 Git 中检出远程分支
前端·git·github
程序视点2 小时前
FDM下载神器:免费多线程下载工具,速度90+M/S,完美替代1DM!
windows·后端
东风西巷2 小时前
猫眼浏览器:简约安全的 Chrome 内核增强版浏览器
前端·chrome·安全·电脑·软件需求
太阳伞下的阿呆2 小时前
npm安装下载慢问题
前端·npm·node.js