前言
什么是深拷贝?
简单理解: 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  | };  |