前言
什么是深拷贝?
简单理解: b 是 a 的一份拷贝,b 中没有对 a 中对象的引用
另一种理解: b 是 a 的一份拷贝,a、b 各自画图,a 与 b 没有连接
简单的方法
JSON 序列化与反序列化
1 | let obj = {a:1,b:[2,3],c:{xx:4,yy:5}} |
2 | let obj2 = JSON.parse(JSON.stringify(obj)) |
3 | obj2.a = 11 |
4 | obj2.b[0] = 11 |
5 | obj2.c.xx = 44 |
6 | console.log(obj) // {a:1,b:[2,3],c:{xx:4,yy:5}} 原先的obj不会被改变 |
缺点:
- 不支持函数
- 不支持 undefind(因为 JSON 只有 null)
- 不支持日期(会把 date 变成 iso8601 格式的日期字符串)
- 不支持正则
- 不支持 Error 对象
- 不支持环状结构
- NaN Infinity 会变成 null
1 | const obj = { |
2 | a: "a", |
3 | b: undefined, |
4 | c: function () { |
5 | |
6 | console.log("this is a fnction"); |
7 | }, |
8 | d: NaN, |
9 | e: Infinity, |
10 | f: new Date(), |
11 | g: /123/, |
12 | h: new Error("error"), |
13 | }; |
14 | console.log(JSON.parse(JSON.stringify(obj))); |
15 |
|
16 |
|
17 | obj.i = obj; |
18 | console.log(JSON.parse(JSON.stringify(obj))); |
自己实现一个深拷贝
复制基本类型
基本类型:
- Number
- String
- Boolean
- Symbol
- undefined
- null
基本类型直接返回就好了
1 | function deepClone(souce) { |
2 | return souce; |
3 | } |
引用类型 递归克隆
1 | function deepClone(source) { |
2 | if (source instanceof Object) { |
3 | const dist = new Object(); |
4 | for (let key in source) { |
5 | dist[key] = deepClone(source[key]); |
6 | } |
7 | return dist; |
8 | } else { |
9 | return source; |
10 | } |
11 | } |
Array 数组对象
如果是数组的话 用上面的方法去拷贝 返回的是一个伪数组 且不会拷贝 length 属性
所以我们的 dist 要是一个数组对象
1 | function deepClone(source) { |
2 | if (source instanceof Object) { |
3 | if (source instanceof Array) { |
4 | |
5 | const dist = new Array(); |
6 | for (let key in source) { |
7 | dist[key] = deepClone(source[key]); |
8 | } |
9 | return dist; |
10 | } else { |
11 | |
12 | const dist = new Object(); |
13 | for (let key in source) { |
14 | dist[key] = deepClone(source[key]); |
15 | } |
16 | return dist; |
17 | } |
18 | } else { |
19 | return source; |
20 | } |
21 | } |
Function 对象
函数的话 有两种方法 第一种就是再封装一个函数去调用源函数
1 | if(source instance of Function){ |
2 | const dist = function () { |
3 | return source.apply(this, arguments); |
4 | }; |
5 | for (let key in source) { |
6 | dist[key] = deepClone(source[key]); |
7 | } |
8 | return dist; |
9 | } |
但是这种方法不会拷贝 length 属性 也就是函数形参个数 可以通过正则匹配函数体 函数参数 然后重新生成函数
1 | const sourceString = source.toString(); |
2 | let body = sourceString.match(/(?<={)((\n*.*)*)(?=})/m)[0]; |
3 | let params = sourceString.match(/(?<=\().*(?=\))/)[0]; |
4 | dist = new Function(...params.split(","), body); |
1 | else if (source instanceof RegExp) { |
2 | dist = new RegExp(source.source, source.flags); |
3 | for (let key in source) { |
4 | dist[key] = deepClone(source[key]); |
5 | } |
6 | return dist |
7 | } |
1 | else if (source instanceof Date) { |
2 | dist = new Date(source); |
3 | for (let key in source) { |
4 | dist[key] = deepClone(source[key]); |
5 | } |
6 | return dist |
7 | } |
1 | a = { name: "a" }; |
2 | a.self = a; |
a 就是环状结构
可以通过缓存的方式去拷贝环结构 在拷贝 a.self 的时候我们已经拷贝过 a 了 可以将 a 与 a 的深克隆结果缓存起来
1 | const cache = [] |
2 | function deepClone(source){ |
3 | if(source instanceof Object){ |
4 | const sourceDist = cache.find((value) => value[0] === source)?.[1]; |
5 | if (sourceDist) { |
6 | return sourceDist; |
7 | } else { |
8 | let dist |
9 | ... |
10 | |
11 | cache.push([source,dist]) |
12 | for (let key in source) { |
13 | dist[key] = deepClone(source[key]); |
14 | } |
15 | return dist |
16 | } |
17 | } |
18 | } |
最终代码
1 |
|
2 | const cache = []; |
3 | module.exports = function deepClone(source) { |
4 | |
5 | if (source instanceof Object) { |
6 | const sourceDist = cache.find((value) => value[0] === source)?.[1]; |
7 | if (sourceDist) { |
8 | return sourceDist; |
9 | } else { |
10 | let dist; |
11 |
|
12 | |
13 | if (source instanceof Array) { |
14 | dist = new Array(); |
15 |
|
16 | |
17 | } else if (source instanceof Function) { |
18 | |
19 | |
20 | |
21 |
|
22 | const sourceString = source.toString(); |
23 | let body = sourceString.match(/(?<={)((\n*.*)*)(?=})/m)[0]; |
24 | let params = sourceString.match(/(?<=\().*(?=\))/)[0]; |
25 | dist = new Function(...params.split(","), body); |
26 |
|
27 | |
28 | } else if (source instanceof RegExp) { |
29 | dist = new RegExp(source.source, source.flags); |
30 |
|
31 | |
32 | } else if (source instanceof Date) { |
33 | dist = new Date(source); |
34 |
|
35 | |
36 | } else { |
37 | dist = new Object(); |
38 | } |
39 |
|
40 | cache.push([source, dist]); |
41 | for (let key in source) { |
42 | |
43 | if (source.hasOwnProperty(key)) { |
44 | dist[key] = deepClone(source[key]); |
45 | } |
46 | } |
47 | return dist; |
48 | } |
49 |
|
50 | |
51 | } else { |
52 | return source; |
53 | } |
54 | }; |