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...

相关推荐
2501_915918412 小时前
Web 前端可视化开发工具对比 低代码平台、可视化搭建工具、前端可视化编辑器与在线可视化开发环境的实战分析
前端·低代码·ios·小程序·uni-app·编辑器·iphone
程序员爱钓鱼2 小时前
Go语言实战案例 — 工具开发篇:实现一个图片批量压缩工具
后端·google·go
程序员的世界你不懂3 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
索迪迈科技3 小时前
网络请求库——Axios库深度解析
前端·网络·vue.js·北京百思可瑞教育·百思可瑞教育
gnip3 小时前
JavaScript二叉树相关概念
前端
一朵梨花压海棠go4 小时前
html+js实现表格本地筛选
开发语言·javascript·html·ecmascript
attitude.x4 小时前
PyTorch 动态图的灵活性与实用技巧
前端·人工智能·深度学习
β添砖java4 小时前
CSS3核心技术
前端·css·css3
ChinaRainbowSea4 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
舒一笑4 小时前
同步框架与底层消费机制解决方案梳理
后端·程序员