532 字
3 分钟
你还在用JSON.stringify深度拷贝对象吗
当对象进行深度拷贝时,一种常见的方法是使用现有的库或编写递归函数来执行复制操作,这些方法可能相对繁琐。许多人会更倾向于使用 JSON.parse/JSON.stringify 来快速实现,但这种方法存在一些缺陷和潜在错误。接下来,我将分析另外两种更好的深度拷贝方式。
作为对比,首先准备了如下测试数据,尽量类型覆盖全面。
const obj = {
// 基本数据类型
num: 1,
str: 'abc',
bool: true,
nul: null,
undf: undefined,
sym: Symbol('1'),
// bign: BigInt(1n),
// 普通对象
obj: {
name: '名称',
age: 20,
},
// 数组
arr: [1, 2, 3],
// 函数
func: function () {
console.log('function')
},
// 时间
date: new Date(),
// 正则
reg: /[0-9]/,
// Map
map: new Map().set('key', 1),
// Set
set: new Set().add('set'),
// Symbol为key
[Symbol('key')]: 1,
}
// 不可枚举属性
Object.defineProperty(obj, 'inumerable', {
enumerable: false,
value: '不可枚举属性',
})
// 原型对象
Object.setPrototypeOf(obj, {
proto: 'proto',
})
// 循环引用
obj.loop = obj
JSON.parse/JSON.stringify
JSON.parse/JSON.stringify
是最常用的深度拷贝方法。
JSON.parse(JSON.stringify(obj))
通过测试,发现如下问题:
- 执行会报错,存在 BigInt 类型、循环引用;
Date
类型会变成字符串;- 键值丢失:值为
Function
、undefined
、Symbol
类型; - 键名丢失:键名为
Symbol
类型; - 键值变成空对象:值为
Map
、Set
、RegExp
类型; - 无法拷贝:不可枚举属性、对象原型链。
MessageChannel
MessageChannel
是用来创建一个消息通道的,可以通过 2 个管道的消息发送和接收进行对象拷贝。
function deepClone(obj) {
return new Promise((resolve) => {
const MC = new MessageChannel()
const port1 = MC.port1
const port2 = MC.port2
port1.onmessage = (e) => {
resolve(e.data)
}
port2.postMessage(obj)
})
}
deepClone(obj).then((res) => {
console.log(res)
})
通过测试发现:
- 执行会报错,值为
Function
、Symbol
类型; - 键名丢失:键名为
Symbol
类型; - 无法拷贝:不可枚举属性、对象原型链。
虽然有一些问题,但整体比JSON.parse/JSON.stringify
结果要好。看下兼容性,大部分主流浏览器都是兼容:
structuredClone
structuredClone
是一个新的深度拷贝方法,它的效果和MessageChannel
一样,并且使用起来更简单,结果也是同步返回:
structuredClone(obj)
但是它的兼容性还不太理想:
你还在用JSON.stringify深度拷贝对象吗
https://fuwari.vercel.app/posts/20230912/