JS 原生的深拷贝来啦——structuredClone

5,417 阅读3分钟

前言

提到 JS 的深拷贝,你的脑海里可能会自动的浮现两个实现方式:

  • JSON.parse(JSON.stringify(obj))

  • lodash 的 cloneDeep

上述两种深拷贝的方式都有一些缺点(本文后续内容会详述),因此在学习上述深拷贝方式的时候,你可能就想过,浏览器为啥不支持原生的深拷贝呢?这不 structuredClone 就来啦~浏览器运行时原生支持的深拷贝 API

JSON.parse(JSON.stringify(obj)) 的缺点

  • 如果你的 obj 对象存在循环引用,那么该方法会报错

image

  • 如果你的 obj 对象存在 Date 对象,该方法会将其转化成字符串

image

  • 如果你的 obj 对象中存在 Set、Map、正则、Error 对象,该方法会将其转成空对象字面量 { } ,如果存在 undefined,该方法会直接忽略

image

见上图所述执行结果,Set、Map、正则、Error 对象 都变成了 { } ,undefined 直接无了。。。

Lodash 可能存在的风险

image

image

Lodash 的 tree-shaking 可能和我们的使用心智有一些出入,如果你的项目脚手架没有处理该问题,你在引入方式也没有注意,那么会带来一些性能损耗。

处理 lodash 的 tree-shaking 可以参考:

lodash按需引入 - 一叶一菩提22 - 博客园

Lodash: tree shaking

常见的两种深拷贝方式都存在一定的问题*(但是也有各自的优点,读者应根据自己对深拷贝的理解结合使用场景去选择适合的深拷贝方法,本文最后也会简单对比三种深拷贝方式)*,由此引出本文的主角 structuredClone:

structuredClone 语法简介

全局的 structuredClone() 方法使用结构化克隆算法将给定的值进行深拷贝

该方法还支持把原始值中的可转移对象转移到新对象,而不是把属性引用拷贝过去。 可转移对象与原始对象分离并附加到新对象;它们不可以在原始对象中访问被访问到。

——MDN

介绍 structuredClone 方法深拷贝的基础用法

const person = {
  name: "Timmy",
  gradutionDate: new Date('2017-06-17'),
  readingBooks: ['Dream of the Red Chamber']
}

const copied = structuredClone(person)

在 console 控制台做个测试:

image

很多情况下,上述结果确实更符合我们开发者的使用心智!

structuredClone 深拷贝的能力如下:

structuredClone 不能做什么:

  • Function 对象是不能被结构化克隆算法复制的;如果你尝试这样子去做,这会导致抛出 DATA_CLONE_ERR 的异常。

  • 企图去克隆 DOM 节点同样会抛出 DATA_CLONE_ERR 异常。

  • 对象的某些特定参数也不会被保留

介绍 structuredClone 方法转移[可转移对象]的用法

语法:

structuredClone(value, { transfer })

当给 structuredClone 方法传入可选参数 transfer 时,可以使 可转移对象 仅被传递,不被克隆。 传输导致原始对象(里的属性)无法继续使用。

var uInt8Array = new Uint8Array(1024 * 1024 * 16); // 16MB
for (var i = 0; i < uInt8Array.length; ++i) {
  uInt8Array[i] = i;
}
const transferred = structuredClone(uInt8Array, { transfer: [uInt8Array.buffer] });
console.log(uInt8Array.byteLength);  // 0

对比三种深拷贝方法

  • JSON.parse(JSON.stringify(obj)) 方法使用简单,且兼容性好,如果你要处理的是简单数据类型的深拷贝,该方法可以 hold 住

  • _.copyDeep 兼容性好,功能强大,对于 Function 类型不会报错,但是缺点是需要注意处理 tree-shaking 否则会有性能损耗

  • structuredClone 方法是浏览器原生支持的,对于 Function 类型会报错,但最主要的缺点是兼容性问题:

    structuredClone 方法垫片:github.com/ungap/struc…

参考文档:

[1] Deep Cloning Objects in JavaScript, the Modern Way

[2] 最新HTML规范——structuredClone深拷贝函数,能取代JSON或者lodash吗? - 掘金

[3] structuredClone() - Web 开发技术 | MDN

[4] 结构化克隆算法 - Web API 接口参考 | MDN

[5] 可转移对象 - Web API 接口参考 | MDN