记录--一种更现代的深浅拷贝方法
Posted 林恒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录--一种更现代的深浅拷贝方法相关的知识,希望对你有一定的参考价值。
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
你是否知道,JavaScript中有一种原生的方法来做对象的深拷贝?
本文我们要介绍的是 structuredClone
函数,它是内置在 JavaScript 运行时中的:
const calendarEvent = title: "前端修罗场", date: new Date(123), attendees: ["Steve"] const copied = structuredClone(calendarEvent)
在上面的示例中,我们不仅拷贝了对象,还拷贝了嵌套数组,甚至拷贝了Date
对象:
copied.attendees // ["Steve"] copied.date // Date: Wed Dec 31 1969 16:00:00 cocalendarEvent.attendees === copied.attendees // false
没错,structuredClone 不仅可以做到以上这些,而且还可以:
- 克隆无限嵌套的对象和数组
- 克隆循环引用
- 克隆各种各样的 JavaScript 类型,如
Date, Set, Map, Error, RegExp, ArrayBuffer, Blob, File, ImageData
等等 - 转移任何可转移的对象
举个例子:
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)
为什么不使用对象扩展运算符进行克隆呢
值得注意的是,我们讨论的是深拷贝。如果你只需要做一个浅拷贝,也就是一个不复制嵌套
对象或数组的拷贝,那么我们可以只做一个对象扩展:
const simpleEvent = title: "前端修罗场", const shallowCopy = ...calendarEvent
或者你也可以使用这种方法:
const shallowCopy = Object.assign(, simpleEvent) const shallowCopy = Object.create(simpleEvent)
但是一旦我们有了嵌套项,我们就会遇到麻烦:
const calendarEvent = title: "前端修罗场", date: new Date(123), attendees: ["Steve"] const shallowCopy = ...calendarEvent shallowCopy.attendees.push("Bob") shallowCopy.date.setTime(456)
如上所见,我们没有对该对象进行完整复制。
嵌套日期和数组仍然是两者之间的共享引用,如果我们想编辑它们,认为我们只是更新复制的日历事件对象,这可能会导致重大问题。
为什么不使用JSON.parse(JSON.stringify(x)) ?
它实际上是一个很棒的工具,性能令人惊讶,但也有一些structuredClone可以解决的缺点。
举个例子:
const calendarEvent = title: "前端修罗场", date: new Date(123), attendees: ["Steve"] const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))
如果我们输出 problematicopy,我们会得到:
title: "前端修罗场", date: "1970-01-01T00:00:00.123Z" attendees: ["Steve"]
这不是我们想要的 date 格式,因为格式应该是date对象,而不是字符串。
这是因为 JSON.Stringify
只能处理基本对象、数组和基本类型。任何其他类型都可能以难以预测的方式处理。例如,日期被转换为字符串。但是 Set 对象就会被简单地转换为 。
同时,JSON.Stringify
甚至会完全忽略某些东西,如 undefined 或 function。
例如,如果我们用这个方法复制下面这个例子:
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))
输出之后,得到的是这样:
"set": , "map": , "regex": , "deep": "array": [ ] , "error": ,
可以看到, 这种方法的拷贝出错了。
因此,如果我们的需求适合这个方法,可以用这个方法。但是,我们可以用 structuredClone
做这个方法有很多不能做的事情。
为什么不是 _.cloneDeep?
到目前为止,Lodash 的 cloneDeep 函数是这个问题的一个非常常见的解决方案。事实上,这确实也像预期的那样工作:
import cloneDeep from \'lodash/cloneDeep\' const calendarEvent = title: "前端修罗场", date: new Date(123), attendees: ["Steve"] // ✅ const clonedEvent = structuredClone(calendarEvent)
但是,这里有一个警告。根据我的 IDE 中的导入成本扩展,打印任何我导入函数的成本,这个函数占了 17.4kb` 的大小(5.3kb gzip):
假设你只导入了这个函数。如果改用更常见的方式导入,没有意识到摇树并不总是按希望的方式工作,那么可能会无意中为这个函数导入高达2 5kb 的文件
以上是关于记录--一种更现代的深浅拷贝方法的主要内容,如果未能解决你的问题,请参考以下文章