JS深拷贝与浅拷贝

深拷贝

JSON转换

1
2
3
const arr = [1, 2, 3, 4, 5];
const clone = JSON.parse(JSON.stringify(arr));
console.info(clone);

使用JSON.parse(JSON.stringify(arr))的方式进行深拷贝时,并不会拷贝函数。

JSON.stringify()方法会忽略JavaScript对象中的函数成员,并将其转换为空值。

因此,在使用JSON.parse(JSON.stringify(arr))时,任何函数成员都将丢失并转换为undefined

使用 JSON.parse(JSON.stringify(arr)) 进行深拷贝的方式相对简单且易于理解,适合用于处理普通的数据结构。

然而,它无法正确地处理一些特殊类型的数据,例如函数、正则表达式、日期对象等,因为这些类型在 JSON 格式中无法正确表示。

MessageChannel

使用MessageChannel实现深拷贝。

MessageChannel除了用作通信还有一些hack的用法,比如用它来做deepClone。

1
2
3
4
5
6
7
8
9
function deepClone(target) {
return new Promise(resolve => {
const channel = new MessageChannel()
channel.port2.postMessage(target)
channel.port1.onmessage = eve => {
resolve(eve.data)
}
})
}

使用

1
2
3
4
5
6
7
8
9
const obj = {
name: '123',
b: {
c: 456
},
d: undefined
}

deepClone(obj).then(d => console.log(d))

这个方法比较优秀的地方在于undefined的不会丢失,循环引用的对象也不会报错,循环点会被置为undefined,不过不能复制函数。

使用 MessageChannel 实现异步深拷贝,可以正确地处理任何类型的数据,包括特殊类型。

由于它是异步的方式,所以性能可能会受到一定影响,特别是在处理大型数据结构时会更明显。

自定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}

const clone = Array.isArray(obj) ? [] : {};

for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}

return clone;
}

使用

1
2
3
4
5
const obj = { a: 1, b: { c: 2 }, d: [3, 4],getValue(){
return this.a;
}};
const clonedObj = deepClone(obj);
console.log(clonedObj);

这种方式会拷贝方法。

浅拷贝

方式1

使用展开运算符可以轻松地复制

1
2
3
const arr = [1, 2, 3, 4, 5];
const clone = [...arr];
console.info(clone);

方式2

使用数组的slice方法复制数组

1
2
3
const arr = [1, 2, 3, 4, 5];
const clone = arr.slice();
console.info(clone);

slice() 是数组对象的一个方法,用于从数组中提取指定位置的元素创建一个新的数组。

slice() 方法不会修改原始数组,而是返回一个浅拷贝(shallow copy)的新数组。

slice() 方法可以接收两个参数,即 startend。起始位置 start 是要提取的起始索引(包含在提取范围内),结束位置 end 是要提取的结束索引(不包含在提取范围内)。

如果省略 end 参数,默认会提取到数组的末尾。