1、深拷贝
基础版
const deepClone = (obj) => {
return JSON.parse(JSON.stringify(obj));
};
1
2
3
2
3
基础版缺点
- 函数、undefined、symbol经过JSON.stringify后会消失;
- Date类型变成字符串;
- 无法拷贝不可枚举的属性,即enumerable为false的属性;
- 无法拷贝对象的原型链;
- RegExp类型会变成空对象;
- NaN、Infinity、-Infinity,经过JSON.stringify后会变成null;
- 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj),会直接报错。
遍历拷贝
const deepClone = (obj) => {
const newObj = {};
for (let key in obj) {
if (typeof obj[key] === "object") {
newObj[key] = deepClone(obj[key]);
} else {
newObj[key] = obj[key];
}
}
return newObj;
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
遍历版缺点
- 不能拷贝不可枚举的属性,即enumerable为false的属性,以及Symbol类型;
- 只能拷贝一般的引用类型,对于Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
- 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj),会爆栈。
优化遍历拷贝
// 判断是否为复杂引用类型
const isRealObj = (obj) =>
(typeof obj === "object" || typeof obj === "function") && obj !== null;
const deepClone = (obj, hash = new WeakMap()) => {
// 处理Date对象
if (obj.constructor === Date) {
return new Date(obj);
}
// 处理RegExp对象
if (obj.constructor === RegExp) {
return new RegExp(obj);
}
// 利用WeakMap处理循环引用
if (hash.has(obj)) {
return hash.get(obj);
}
// 获取对象所有属性的特性列表
const allKeyValue = Object.getOwnPropertyDescriptors(obj);
// 继承原型链
const newObj = Object.create(Object.getPrototypeOf(obj), allKeyValue);
// 利用WeakMap处理循环引用
hash.set(obj, newObj);
// 使用Reflect.ownKeys获取对象的所有属性数组,包括不可枚举属性
for (let key of Reflect.ownKeys(obj)) {
// 为复杂引用类型且不为function类型的话进行递归,并将同一个WeakMap传入
newObj[key] =
isRealObj(obj[key]) && typeof obj[key] !== "function"
? deepClone(obj[key], hash)
: obj[key];
}
return newObj;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
优化点
- 使用Reflect.ownKeys方法取出不可枚举属性以及Symbol类型;
- 对于Date、RegExp类型,直接生成一个新的实例返回;
- 使用Object.getOwnPropertyDescriptors()获取所有属性及其特性,并使用Object.create()创建一个新对象,继承传入原对象的原型链;
- 使用WeakMap类型作为Hash表,因为WeakMap是弱引用类型,可以有效防止内存泄漏,检测循环引用,遇到循环引用直接返回WeakMap存储的值。