JS中Deep Clone的几种方式

在某些业务中,我们为了不修改原来的数组或对象,我们基本会选择复制一份,进行处理。

对于数组,我们会使用 [].concat(arr), arr.slice(0)等方法来进行复制
对于对象,我们会使用Object.assign({}, obj), {...obj}等方法进行复制处理.

但上面都是浅拷贝,如果数组或对象中的元素是简单类型,那这样不会有问题,如果是复杂类型呢?

让我们看一段代码:

1
2
3
4
5
6
7
8
// 原数组
const arr = [{ name: 'yutao', age: 24 }];
// 拷贝复制
const newArr = arr.slice(0);
// 对新数组操作
newArr[0].age = 25;

arr[0].age = 24 ?

这时候我们发现arr[0].age已经被修改成了25,这样导致对原数组造成了破坏,某些情况下我们会发生无法预测的bug。

这是由于,浅拷贝,如果遇到了复杂类型(对象、数组),只会拷贝指针地址,并没有把内容完整的拷贝出来,从而导致我们修改新数组中的内容,对原数组进行了影响。这也不难理解为何称为浅拷贝了。

但是,如果我们想修改新数组中的内容,却又不对原数组造成影响,那我们就要使用深拷贝了,下面将列举一些深拷贝的方式,就当是记录,以便不时之需。

  1. 暴力的JSON

首先当然要祭出最方便、最快捷的方式 JSON.parse(JSON.stringify(obj))

这个方法,目前很通用,不过对一些类型支持不友好:Set,Map, WeakSet, WeakMap, Date, RegExp, (Blob, FileList, ImageData 没有测试过)。也就是如果你需要复制的对象包含上面的类型属性,那么你不能使用这种方法,因为你会得到以下内容:

会有问题的类型

我们可以看到,类型已经变了,但是如果没有以上类型的话,本方法还是非常便捷的。

  1. 不嫌累的复制

这种方法选择的是递归复制,如果是复杂类型,则继续拷贝直到简单类型,这里就不贴上代码了。( ̄▽ ̄)”

  1. 结构化复制

使用标准定义的方法,structuredClone(目前还不支持),但是目前已有下面几种方法可以实现结构化复制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1) history.state

const deepClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const cloneObj = history.state;
history.replaceState(oldState, null);
return cloneObj;
}

// 2) Notification
const deepClone = obj => {
const notify = new Notification('', { data: obj, silent: true });
notify.onshow = notify.close.bind(notify);
return notify.data;
};

通过测试,我们发现我们实现了支持类型的深拷贝:

history.state方式

Notification方式

欢迎进行补充 O(∩_∩)O

推荐文章