实现一个deepClone方法

# 深拷贝和浅拷贝的定义
# 浅拷贝
创建一新对象,有着原对象属性值的一份精确拷贝。若属性是基本类型,拷贝的是基本类型的值,若属性是引用类型,拷贝的是内存地址。
若其中一对象改变了这个地址,会影响到另一个对象。
# 深拷贝
将一个对象从内存中完整拷贝一份出来,从堆内存中开辟一新区域存放新对象,且修改新对象不影响原对象。
# 乞丐版
JSON.parse(JSON.stringify())
1
写法简单,可应对大部分场景,但拷贝其他引用类型、函数、循环引用等,是不行的
# 基础版本
function clone(target) {
if (typeof target === 'object') {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
考虑的object,但是数组等依然不兼容
# 考虑数组
function clone(target) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
兼容数组,但是循环引用会导致死循环。
# 循环引用
解决循环引用问题,需额外开辟一个存储空间Map,存储当前对象和拷贝对象的对应关系。 当需拷贝当前对象时,先去存储空间中找,有无拷贝过此对象,若有直接返回,若无,继续拷贝。 注意: 这里用的是WeakMap WeakMap 对象时一组键/值对的集合,其中的键是弱引用。其键必是对象,而值可是任意的。 弱引用与强引用相对。一对象若只被弱引用所引用,则被认为是不可访问(或弱可访问),垃圾回收机制会自动帮我们回收。 当我们拷贝的对象非常庞大时,用Map会对内存造成非常大的额外消耗,且需手动消除Map属性才能释放这块内存,而WeakMap会帮我们解决。
function clone(target, map = new WeakMap()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) return target;
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
}
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8]
};
target.target = target;
console.log(clone(target));
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
# 性能优化
从图中了解,forin效率非常低,while效率非常高。
function clone(target, map = new WeakMap()) {
if (typeof target === 'object') {
const isArray = Array.isArray(target);
let cloneTarget = isArray ? [] : {};
if (map.get(target)) return target;
map.set(target, cloneTarget);
const keys = isArray ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) key = value;
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
} else {
return target;
}
}
function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
}
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
# 其他数据类型
- 合理的 判断引用类型
- 获取数据类型
- 可继续遍历的类型
- 不可继续遍历的类型
- 克隆函数
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]';
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const numberTag = '[object Number]';
const regexpTag = '[object RegExp]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const funcTag = '[object Function]';
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
}
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
function getType(target) {
return Object.prototype.toString.call(target);
}
function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}
function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
}
function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
}
function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(funcString);
}
}
function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
default:
return null;
}
}
function clone(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) return target;
// 初始化
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}
// 防止循环引用
if (map.get(target)) return target;
map.set(target, cloneTarget);
// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value));
});
return cloneTarget;
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value));
});
return cloneTarget;
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) key = value;
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
const map = new Map();
map.set('key', 'value');
map.set('ConardLi', 'code秘密花园');
const set = new Set();
set.add('ConardLi');
set.add('code秘密花园');
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8],
empty: null,
map,
set,
bool: new Boolean(true),
num: new Number(2),
str: new String(2),
symbol: Object(Symbol(1)),
date: new Date(),
reg: /\d+/,
error: new Error(),
func1: () => {
console.log('code秘密花园');
},
func2: function (a, b) {
return a + b;
}
};
console.log(clone(target));
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
上次更新: 2022/09/07, 15:54:26