-
Notifications
You must be signed in to change notification settings - Fork 0
/
深拷贝.js
164 lines (152 loc) · 4.17 KB
/
深拷贝.js
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
161
162
163
164
/**
* 深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
*/
/**
* JSON.parse 方法
*
* 缺点:
* 1. 他无法实现对函数 、RegExp等特殊对象的克隆
* 2. 会抛弃对象的constructor,所有的构造函数会指向Object
* 3. 对象有循环引用,会报错
*/
function deepClone(target) {
return JSON.parse(JSON.stringify(target));
}
/**
* 基础版本
* 1. 考虑到数组类型
* 2. 深层遍历
*/
function deepClone(target) {
if (typeof target !== "object" || target === null) {
return target;
}
const cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) {
cloneTarget[key] = deepClone(target[key]);
}
return cloneTarget;
}
/**
* 升级版本
* 1. 考虑循环引用,使用键值为弱引用类型的 WeakMap 对象,缓存已遍历对象
* 2. 性能优化,使用 while 循环语句代替 for in
*/
const forEach = (array, iterator) => {
let index = -1;
while (++index < array.length) {
iterator(array[index], index);
}
};
function deepClone(target, map = new WeakMap()) {
if (typeof target !== "object" || target === null) {
return target;
}
const isArray = Array.isArray(target);
const cloneTarget = isArray ? [] : {};
if (map.has(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
const keys = isArray ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = deepClone(target[key], map);
});
return cloneTarget;
}
/**
* 最终版本
* 考虑深拷贝类型:Set、Map、Function、Array、Object、Arguments 类型
* 考虑特殊拷贝类型:Boolean、Number、String、Error、Date、RegExp、Symbol 类型
*/
const deepCloneMapping = ["Set", "Map", "Array", "Object", "Arguments"];
const getType = target => Object.prototype.toString.call(target).slice(8, -1);
const forEach = (array, iterator) => {
let index = -1;
while (++index < array.length) {
iterator(array[index], index);
}
};
const cloneReg = targe => {
const regFlags = /\w*$/;
const result = new targe.constructor(targe.source, regFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
};
const cloneSymbol = target => Object(Symbol.prototype.valueOf.call(targe));
const cloneFunction = target => {
if (!target.prototype) return eval(funcString);
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = target.toString();
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const params = param[0].split(",");
return new Function(...params, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
};
const cloneOtherType = (targe, type) => {
switch (type) {
case "Boolean":
case "Number":
case "String":
case "Error":
case "Date":
return new targe.constructor(targe);
case "RegExp":
return cloneReg(targe);
case "Symbol":
return cloneSymbol(targe);
case "Function":
return cloneFunction(targe);
default:
return null;
}
};
function deepClone(target, map = new WeakMap()) {
const type = getType(target);
const isArray = type === "Array";
let cloneTarget = isArray ? [] : {};
if (target && typeof target !== "object") {
return target;
}
if (deepCloneMapping.includes(type)) {
cloneTarget = new target.constructor();
} else {
return cloneOtherType(target, type);
}
if (map.has(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
if (type === "Set") {
target.forEach(value => {
cloneTarget.add(deepClone(value));
});
return cloneTarget;
}
if (type === "Map") {
target.forEach((value, key) => {
cloneTarget.set(key, deepClone(value));
});
return cloneTarget;
}
const keys = isArray ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = deepClone(target[key], map);
});
return cloneTarget;
}