-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdeepClone.js
211 lines (192 loc) · 5.41 KB
/
deepClone.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
deepClone
简易 API 版: JSON.parse(JSON.stringify())
存在的问题:
a. 循环引用问题
b. 特殊对象
c. 函数的拷贝
*/
// import getType from './getType'; //这里是直接从 那个文件里粘贴过来的,为了方便控制台调试
const getType = value => {
// 对象为 null
if (value === null) {
return value + '';
}
// 对象为引用类型(除函数外
if (typeof value === 'object') {
const toString = Object.prototype.toString;
return toString.call(value).slice(8, -1).toLowerCase();
} else {
//为基本类型或者函数
return typeof value;
}
};
/**
* 判断是否为对象(包括函数)
* @param {any} target 要判断的变量
* @returns {boolean} 是否为对象(包括函数)
*/
const isObject = target =>
(typeof target === 'object' || typeof target === 'function') &&
target !== null;
/* 可遍历的类型 */
const canTraverse = {
map: true,
set: true,
array: true,
object: true,
arguments: true,
};
const mapTag = 'map';
const setTag = 'set';
const boolTag = 'boolean';
const numberTag = 'number';
const stringTag = 'string';
const symbolTag = 'symbol';
const dateTag = 'date';
const errorTag = 'error';
const regexpTag = 'regexp';
const funcTag = 'function';
const handleRegExp = target => {
const { source, flags } = target;
return new target.constructor(source, flags);
};
const handleFunc = func => {
// 箭头函数直接返回自身
if (!func.prototype) return func;
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
// 分别匹配 函数参数 和 函数体
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (!body) return null;
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
};
/**
* 对不可遍历的对象执行不同的处理
* @param {any} target 不能遍历的对象
* @param {string} tag 处理标志
* @returns {object} 拷贝得到的对象
*/
const handleNotTraverse = (target, tag) => {
const Ctor = target.constructor;
switch (tag) {
case boolTag:
/* ES6 不推荐 new 基础类型 写法 so: */
/* valueOf 主要为了 Boolean 类型不会有 bug */
return new Object(Boolean.prototype.valueOf.call(target));
case numberTag:
return new Object(Number.prototype.valueOf.call(target));
case stringTag:
return new Object(String.prototype.valueOf.call(target));
case symbolTag:
return new Object(Symbol.prototype.valueOf.call(target));
case errorTag:
case dateTag:
return new Ctor(target);
case regexpTag:
return handleRegExp(target);
case funcTag:
return handleFunc(target);
default:
return new Ctor(target);
}
};
/**
* 深拷贝
* @param {any} target 待拷贝的对象
* @param {WeakMap} cache 用于解决循环引用问题
* @returns {any} 拷贝完的对象
*/
const deepClone = (target, cache = new WeakMap()) => {
if (!isObject(target)) return target;
let type = getType(target);
let cloneTarget;
if (!canTraverse[type]) {
return handleNotTraverse(target, type);
} else {
// 保证对象的原型不丢失
let ctor = target.constructor;
cloneTarget = new ctor();
}
// if (cache.get(target)) return target;
// cache.set(target, true);
if (cache.has(target)) return cache.get(target);
cache.set(target, cloneTarget);
if (type === mapTag) {
target.forEach((item, key) => {
cloneTarget.set(deepClone(key, cache), deepClone(item, cache));
});
}
if (type === setTag) {
target.forEach(item => {
cloneTarget.add(deepClone(item, cache));
});
}
// 处理数组和对象
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], cache);
}
}
return cloneTarget;
};
//test
const mp = new Map([
[1, 2],
['1', 2],
]);
const st = new Set([1, 2, 3]);
const f = function () {
console.log('f');
};
const af = () => void console.log('af');
const obj = { s: [1, 2] };
obj.ss = obj;
const a = [1, '1', mp, st, f, af, obj, false];
const b = deepClone(a);
console.log(b); //太长不放注释里了
a[2].set(9, 0);
console.log(a[2]); //Map { 1 => 2, '1' => 2, 9 => 0 }
console.log(b[2]); //Map { 1 => 2, '1' => 2 }
a[3].add('o');
console.log(a[3]); //Set { 1, 2, 3, 'o' }
console.log(b[3]); //Set { 1, 2, 3 }
a[4] = function () {
console.log('update');
};
a[4](); //update
b[4](); //f
a[5](); //af
b[5](); //af
a[6].test = 'test';
console.log(a[6]); //{ s: [ 1, 2 ], ss: [Circular], test: 'test' }
console.log(b[6]); //{ s: [ 1, 2 ], ss: { s: [ 1, 2 ], ss: [Circular], test: 'test' } }
console.log(a[7]); //false
console.log(b[7]); //false
//循环引用bug
const a1 = {};
a1.self = a1;
const a2 = deepClone(a1);
console.log(a1.self !== a2.self); //true
/*
之前
```js
if (cache.get(target)) return target;
cache.set(target, true);
```
击中缓存后返回 target 本身,导致遍历 a1 self 属性为 a1 时——循环引用时直接返回的是 a1 本身
那就等于浅拷贝了一个对象属性
实际上不应该如此,应该返回的是 a1 的深拷贝
所以 缓存中应该存储 cloneTarget,击中缓存后返回 cloneTarget 才是深拷贝👍
```js
if (cache.has(target)) return cache.get(target);
cache.set(target, cloneTarget);
```
*/