# 用递归的方式手写实现深拷贝
我们先来看网上最常见的深拷贝实现代码
function deepClone(obj) {
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
let item = obj[key];
if (item instanceof Object) {
newObj[key] = deepClone(item);
} else {
newObj[key] = item;
}
}
return newObj;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
测试用例
function Obj() {
this.num = 1,
this.str = 'abc',
this.strObj = new String('def'),
this.reg = /abc/,
this.date = new Date(),
this.arr = [1, 2, 3],
this.child = {
id: '1-1',
child: {
id: '1-1-1'
}
}
return this;
}
Obj.prototype.p = '原型上的属性p';
let obj = new Obj();
let newObj = deepClone(obj);
// 输出原对象
console.log(obj);
console.log(obj.__proto__.p);
// 输出克隆对象
console.log(newObj);
console.log(newObj.__proto__.p);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
可以发现它的特点有,
对于正则表达式、日期类型无法拷贝,
原始值包装类型,如new String('abc')
等不能正常拷贝,
非私有变量,obj
原型上的属性不会继承,而是克隆在了自身属性上。
针对以上情况,有如下改进版,
其中,高亮第4行、第7行解决了非私有变量的继承问题,
第13~16行对正则表达式和日期进行额外的判断处理。
function deepClone(obj) {
if (obj === null) return null;
// 创建一个使用同一个原型的空对象或数组
let newObj = new obj.constructor;
for (let key in obj) {
// 如果是私有属性才拷贝
if (obj.hasOwnProperty(key)) {
let item = obj[key];
// 如果是函数或基本类型,则直接引用或复制,
// 否则就是对象类型(正则、日期、数组和对象,排除函数)
if (typeof item !== 'object') {
newObj[key] = item;
} else if (item instanceof RegExp) {
newObj[key] = new RegExp(item);
} else if (item instanceof Date) {
newObj[key] = new Date(item);
} else {
newObj[key] = deepClone(item);
}
}
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
但对于new String('abc')
等,还是不能正常拷贝,
最后,我尝试进行了一些修改,
function deepClone(obj) {
if (obj === null) return null;
// 创建一个使用同一个原型的空对象或数组
let newObj = new obj.constructor;
for (let key in obj) {
// 如果是私有属性才拷贝
if (obj.hasOwnProperty(key)) {
let item = obj[key];
// 如果是函数或基本类型,则直接引用或复制,
// 否则就是对象类型(正则、日期、数组、object和原始值包装类型,排除函数)
if (typeof item !== 'object') {
newObj[key] = item;
} else if (item instanceof Array) {
newObj[key] = deepClone(item);
// 只有是对象类型中的Object才满足if条件
} else if (Object.prototype.toString.call(item) === '[object Object]') {
newObj[key] = deepClone(item);
// 以下是正则、日期和各类包装类型,用其构造函数实例化一个原来值的实例,这样可以不用分别判断
} else {
newObj[key] = new item.constructor(item);
}
}
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
以上三个版本,均没有实现对函数的深拷贝,只是引用,貌似也没有这个需求吧。。。
当然,以上的几种深拷贝方法没有绝对的对错,一切要看项目需要考虑到什么程度啦!