# 一. 基础
浅拷贝 :只是将数据中所有的数据引用下来,依旧指向同一个存放地址,拷贝之后的数据修改之后,也会影响到原数据的中的对象数据。另外一种说法:只拷贝第一层的原始类型值,和第一层的引用类型地址。如对象的
Object.assign(),数组的Array.prototype.slice()和Array.prototype.concat(),ES6 的扩展运算符深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
# 二. 浅拷贝实现
# 实现一:
function clone(target) {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = target[key];
}
return cloneTarget;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 三. 深拷贝实现
# 实现 1:普通版
JSON.parse(JSON.stringify());
1
# 实现 2:考虑普通的 object
function clone(target) {
if (typeof target === "object") {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
}
// 测试
const target = {
field1: 1,
field2: undefined,
field3: "ConardLi",
field4: {
child: "child",
child2: {
child2: "child2",
},
},
};
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
缺点: 没有考虑到数组
# 实现 3:考虑数组
module.exports = 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
缺点: 没有考虑对象存在循环引用的情况,递归进入死循环导致栈内存溢出
# 实现 4:循环引用
额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝。
这个存储空间,需要可以存储 key-value 形式的数据,且 key 可以是一个引用类型,我们可以选择 Map 这种数据结构:
- 检查 map 中有无克隆过的对象
- 有 - 直接返回
- 没有 - 将当前对象作为 key,克隆对象作为 value 进行存储
- 继续克隆
function clone(target, map = new Map()) {
if (typeof target === "object") {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
改进:使用WeakMap,弱引用。弱引用对象,垃圾回收机制会自动回收。
function clone(target, map = new WeakMap()) {
// ...
}
1
2
3
2
3
# 实现 5:可继续遍历的类型,Map,Set
function getType(target) {
return Object.prototype.toString.call(target);
}
function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}
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);
}
// 防止循环引用
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
// 克隆set
if (type === setTag) {
target.forEach((value) => {
cloneTarget.add(clone(value, map));
});
return cloneTarget;
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map));
});
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 target = {
field1: 1,
field2: undefined,
field3: {
child: "child",
},
field4: [2, 4, 8],
empty: null,
map,
set,
};
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
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
# 实现 6:不可继续遍历的类型
Bool,Number,String,Date,Error类型:
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
克隆Symbol类型:
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 实现 7:克隆函数
function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
console.log("普通函数");
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
console.log("匹配到函数体:", body[0]);
if (param) {
const paramArr = param[0].split(",");
console.log("匹配到参数:", paramArr);
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(funcString);
}
}
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;
},
};
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
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