方法一

  1. const deepClone = (a) => JSON.parse(JSON.stringify(a))

缺点:

  • 不支持 undefined, 函数Date, 正则 等数据
  • 不支持 循环引用

方法二

要对一个数据进行深拷贝,就需要对它的类型进行判断,JS中一共有8种数据类型, 其中复杂类型为 object, 其余7种为简单类型(string, number, undefined, null, boolean, bigint, symbol), 在 object 中又可以分为 Oject, Array, Function, Date, RegExp 等类,由此我们可以写成一个初始版本的深拷贝

  • 如果传入的参数是基础类型则直接返回
  • 如果传入的参数是object则对类进行判断
    • 如果是函数需要判断它是普通函数还是箭头函数
    • 如果是日期,则返回 new Date(a - 0), 其中 a-0 得到的是时间戳
    • 如果是正则,则返回 new RegExp(a.source, a.flags)
    • 如果是数组,则将结果初始化为 []
    • 如果是对象,则将结果初始化为 {}
  • 接着遍历参数 a 的 key, 且用 hasOwnPropery 方法过滤 a 本身的 key, 对每个 key 递归调用深拷贝

    1. function deepClone(a) {
    2. if (a instanceof Object) {
    3. let result
    4. if (a instanceof Array) {
    5. result = []
    6. } else if (a instanceof Function) {
    7. if (a.prototype) {
    8. result = function(){return a.apply(this, arguments)}
    9. } else {
    10. result = (...args) => a.call(undefined, ...args)
    11. }
    12. } else if (a instanceof Date) {
    13. result = new Date(a - 0)
    14. } else if (a instanceof RegExp) {
    15. result = new RegExp(a.source, a.flags)
    16. } else {
    17. result = {}
    18. }
    19. for (let key in a) {
    20. if (a.hasOwnProperty(key)) {
    21. result[key] = deepClone(a[key])
    22. }
    23. }
    24. return result
    25. } else {
    26. return a
    27. }
    28. }

    使用下面的代码测试,涉及到的类型都能进行深拷贝 ```javascript const a = { number: 1, bool: false, str: ‘hi’, empty1: undefined, empty2: null, array: [ {name: ‘jack’, age: 23}, {name: ‘rose’, age: 24} ], date: new Date(2000, 0, 1, 20, 30, 0), regexp: /.(j|t)sx/i, obj: {name: ‘jack’, age: 18}, f1: (a, b) => a + b, f2: function(a, b) {return a + b} }

const b = deepClone(a)

  1. 但是我们还没有考虑循环引用的情况,即当再添加一行下列代码,深拷贝就会报错了,会导致无限递归
  2. ```javascript
  3. a.self = a

为了解决循环引用的问题,我们可以使用 Map 将进行过深拷贝的object类型存起来,在每次深拷贝object类型前都判断在 Map 中是否存在,若存在则直接返回 Map 中的值, 修改后的 deepClone 如下

  1. function deepClone(a, map=new Map()) {
  2. if (a instanceof Object) {
  3. if (map.get(a)) {
  4. return map.get(a)
  5. }
  6. let result
  7. if (a instanceof Array) {
  8. result = []
  9. } else if (a instanceof Function) {
  10. if (a.prototype) {
  11. result = function(){return a.apply(this, arguments)}
  12. } else {
  13. result = (...args) => a.call(undefined, ...args)
  14. }
  15. } else if (a instanceof Date) {
  16. result = new Date(a - 0)
  17. } else if (a instanceof RegExp) {
  18. result = new RegExp(a.source, a.flags)
  19. } else {
  20. result = {}
  21. }
  22. map.set(a, result)
  23. for (let key in a) {
  24. if (a.hasOwnProperty(key)) {
  25. result[key] = deepClone(a[key], map)
  26. }
  27. }
  28. return result
  29. } else {
  30. return a
  31. }
  32. }

在这里更加推荐使用 WeakMap,WeakMap只接受对象作为键名,且WeakMap的键名是对象的弱引用, 其所对应的对象可以被自动垃圾回收,当被垃圾回收时,WeakMap会自动移除该键值对。