在JavaScript中,实现深拷贝的形式有很多种,每种形式都有其长处和毛病。明天介绍一种原生JavaScript提供的structuredClone实现深拷贝。

上面列举一些常见的形式,以及它们的代码示例和优缺点:

1. 应用JSON.parse(JSON.stringify(obj))

代码示例:

function deepClone(obj) {    return JSON.parse(JSON.stringify(obj));}

长处:简单易行,对于大多数对象类型无效。

毛病:不能复制原型链,对于蕴含循环援用的对象可能呈现问题。比方以下代码:

const calendarEvent = {  date: new Date()}const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))

最终失去的date不是Data对象,而是字符串。

{    "date": "2024-03-02T03:43:35.890Z"}

这是因为JSON.stringify只能解决根本的对象、数组。任何其余类型都没有按预期解决。例如,日期转换为字符串。Set/Map只是转换为{}

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": {},}

2. 应用递归

代码示例:

function deepClone(obj) {    if (obj === null || typeof obj !== 'object') {        return obj;    }    let clone = obj.constructor();    for (let attr in obj) {        if (obj.hasOwnProperty(attr)) {            clone[attr] = this.deepClone(obj[attr]);        }    }    return clone;}

长处:对于任何类型的对象都无效,包含循环援用。

毛病:对于大型对象可能会耗费大量内存,并可能导致堆栈溢出。

3. 第三方库,如 lodash 的 _.cloneDeep 办法

代码示例:

const _ = require('lodash');function deepClone(obj) {    return _.cloneDeep(obj);}

长处:反对更多类型的对象和库,例如,反对 Proxy 对象。

毛病:会引入依赖导致我的项目体积增大。

因为这个函数会导致17.4kb的依赖引入,如果只是引入lodash会更高。

4. 古代深拷贝structuredClone

在古代浏览器中,能够应用 structuredClone 办法来实现深拷贝,它是一种更高效、更平安的深拷贝形式。

以下是一个示例代码,演示如何应用 structuredClone 进行深拷贝:

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 = kitchenSinkconst clonedSink = structuredClone(kitchenSink)

structuredClone能够做到:

  • 拷贝有限嵌套的对象和数组
  • 拷贝循环援用
  • 拷贝各种各样的JavaScript类型,如DateSetMapErrorRegExpArrayBufferBlobFileImageData

哪些不能拷贝:

  • 函数
  • DOM节点
  • 属性形容、settergetter
  • 对象原型链

所反对的残缺列表:

ArrayArrayBufferBooleanDataViewDateError类型(上面具体列出的类型)、MapObject,但仅限于一般对象、原始类型,除了symbol(又名numberstringnullundefinedbooleanBigInt)、RegExpSetTypedArray

Error类型:

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

值得庆幸的是 structuredClone 在所有支流浏览器中都受反对,也反对Node.js和Deno。

最初

咱们当初终于能够间接应用原生JavaScript中的structuredClone能力实现深度拷贝对象。每种形式都有其优缺点,具体应用形式取决于你的需要和指标对象的类型。

参考

  • Deep Cloning Objects in JavaScript, the Modern Way(www.builder.io/blog/structured-clone)
  • mozilla structuredClone(developer.mozilla.org/zh-CN/docs/Web/API/structuredClone)

看完本文如果感觉有用,记得点个赞反对,珍藏起来说不定哪天就用上啦~

专一前端开发,分享前端相干技术干货,公众号:南城大前端(ID: nanchengfe)