写在前面

在讨论深拷贝之前要先搞清楚一些概念:

什么是拷贝?
拷贝就是复制

拷贝的分类
拷贝,即复制分为两种,一个是深拷贝,一个是浅拷贝,深拷贝是对应于 JS 中的值数据类型的复制,直接就是复制的实际的值。而当遇到引用数据类型,即对象的复制时,如果仅仅用浅拷贝则拷贝的是引用的地址,而不是引用对象本身。

深拷贝的方法?
如下所示,这里主要介绍两种

1. JSON.parse(JSON.stringify(obj))

利用 JSON 对象先将对象序列化(即字符串化),再将其反序列化(即转化为对应的JS数据类型),即可得到一个深拷贝的对象。

  1. let obj1 = {
  2. a: 1,
  3. b: 2
  4. }
  5. let obj3 = JSON.parse(JSON.stringify(obj1))
  6. obj3.b = 5
  7. console.log(obj1) //{ a: 1, b: 2 }
  8. console.log(obj1 === obj3) //false

但是JSON的方式有局限性,就是对象必须遵从JSON的格式,当遇到层级较深,且序列化对象不完全符合JSON格式时,使用JSON的方式进行深拷贝就会出现问题。

  1. let obj1 = {
  2. a: '1',
  3. b: '2',
  4. c: function func() {}
  5. }
  6. let obj4 = JSON.parse(JSON.stringify(obj1))
  7. console.log(obj4) //{ a: '1', b: '2' }出错

2. 递归深拷贝

用递归的方法实现深拷贝

深拷贝需要考虑的事情:

  1. 递归
  2. 判断类型
  3. 不拷贝原型上的属性
  4. 检查环
  1. function deepClone(source, hash = new Map()) {
  2. if(hash.has(source))) {
  3. return hash.get(source);
  4. }
  5. if(source instanceof Object) {
  6. let result = {};
  7. if(source instanceof Function) {
  8. // 箭头函数不能用作构造函数,因此没有 prototype 原型
  9. if(source.prototype) {
  10. result = function(...args) {
  11. source.call(this, ...args);
  12. }
  13. } else {
  14. result = (...args) => {
  15. source(...args);
  16. }
  17. }
  18. } else if(source instanceof Array) {
  19. result = [];
  20. } else if(source instanceof Date) {
  21. result = new Date(source);
  22. } else if(source instanceof RegExp) {
  23. result = new RegExp(source.source, source.flags);
  24. }
  25. hash.set(source, result);
  26. for(let key in source) {
  27. if(source.hasOwnProperty(key)) {
  28. result[key] = deepClone(source[key])
  29. }
  30. }
  31. return result;
  32. } else {
  33. return source;
  34. }
  35. }