2023年8月9日
By: Chase
聊聊JS的深拷贝structuredClone
前言
在线运行js需要能qiang
在某处看到现在可以用structuredClone
实现深拷贝了, 就针对它进行了一些了解和对比.
structuredClone兼容性
最新的主流浏览器都能用, 发现普遍都是22年上半年之后的版本才支持. 因为现阶段我的项目主要是electron, 浏览器版本很固定, 可以拿来用用. 传统的互联网项目还是慎用吧.
我想查查这是什么时候发布的新特性, 一搜网上一些文章说这是ES2021标准
里新发布的, 但是我去查了下标准, 并没有.
之后又搜了下为什么这个不是ES2021标准
, github上有个issue讨论这个, 但是我没怎么看懂. anyway, 我能用就先用用看.
测试
// 测试是否深拷贝
const testObj = { a: 1, b: { val: 2} }
const cpObj = structuredClone(testObj)
cpObj.b.val = 3
console.log('cpObj', cpObj)
console.log('testObj', testObj)
拷贝类型支持
structuredClone
比JSON
转化进步的一点, 是不会把一些数据搞丢.支持类型
// 测试
const obj = {
a: undefined,
b: /\d+/g,
c: "hahaha"
}
console.log(structuredClone(obj))
console.log(JSON.parse(JSON.stringify(obj)))
// 传递了不能clone的数据类型会报错
obj.d = () => console.log(123)
console.log(structuredClone(obj))
性能比较
在不考虑Object key里有RegExp
, 函数
等的情况下, 我一般就用JSON.parse(JSON.stringify(obj))
深拷贝.
或者对Object递归拷贝, lodash的cloneDeep
就是基于这个思路封装的.
下面和他俩横向比较一下性能.
tips: 这里需要自己F12
打开调试窗口看console.time
的输出了
// 定义一个loop测试函数, 增加总耗时
const testPerformanceFunc = ({
testFunc, // 测试函数
timeLabel = 'time', // console的标签
times = 1e6 // 循环次数 (尽量不要超过100W次, 会卡的)
}) => {
console.time(timeLabel)
for (let i = 0; i < times; i +=1 ) {
testFunc()
}
console.timeEnd(timeLabel)
}
// 测试数据
const testObj = { a: 1, b: { val: 2 }}
// 分别测试
testPerformanceFunc({
testFunc: () => JSON.parse(JSON.stringify(testObj)),
timeLabel: 'JSON拷贝'
})
testPerformanceFunc({
testFunc: () => structuredClone(testObj),
timeLabel: 'structuredClone'
})
testPerformanceFunc({
testFunc: () => _.cloneDeep(testObj),
timeLabel: 'lodash deep clone'
})
我的测试结果截图:
分别为循环10w 100w 1000w
次, 可以看出structuredClone
性能消耗基本上是JSON转换
和lodash
的三倍
结论
单层结构拷贝, 继续用方便的{...obj}
解构复制就可以.
复杂结构深拷贝, 从数据类型兼容性, 性能方面, 浏览器兼容多方面综合看,loadsh
(或自己封装个递归)依然是最优选.