1 什么是深拷贝

  • b是a的一份拷贝,且b中没有对a中对象的引用

2 JSON序列化与反序列化

  • const b = JSON.parse(JSON.stringify(a))

  • 最简单,但存在如下缺点

    • 不支持函数,会直接忽略函数属性

    • 不支持undefined,也会直接忽略

    • 不支持引用,如果a对象中存在a.self = a,即自己引用自己,执行序列化时会报错,因为JSON不支持环状结构,只支持树形结构。

    • 不支持Date类型,会转换成ISO8601字符串(“2021-11-23T08:37:34.035Z”)

    • 不支持正则表达式,会变成空对象

    • …只要是JSON不支持的类型,都可以作为此方法的缺点。

3 递归

  • 看节点的类型

    • 如果是基本类型(除object)就直接返回即可

      • 基本类型(8):number、boolean、string、null、undefined、symbol、bigint、(object)
    • 如果是object就分情况讨论

      • Array、Function(普通函数和箭头函数)、Date、RegExp
      • 对于object类型的对象,要递归深拷贝其内部的全部自有属性
  • 对于a.self = a的自引用情况,可以使用一个Map来记录拷贝的属性(key和value均为拷贝的值),如果发现自引用了,就直接返回a

 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
// 利用Map对每一次拷贝做记录
const cache = new Map()
const deepClone = (v) => {
  if(v instanceof Object) {// object
    // 如果发现自引用(key对象存在),直接返回v
    if(cache.get(v)){ return cache.get(v)}
    let result = undefined
    if(v instanceof Array) {// object-Array
      result = new Array()
    } else if(v instanceof Function){// object-Function
      if(v.prototype) {// 普通函数(都有prototype属性)
        result = function(){ return v.apply(this, arguments)}
      } else {// 箭头函数
        result = (...args) => { return v.call(undefined, ...args)}
      }
    } else if(v instanceof Date) {// object-Date
      // Date的数据减0会转为时间戳,再利用这个时间戳构造新的Date
      result = new Date(v - 0)
    } else if(v instanceof RegExp) {// object-RegExp
      result = new RegExp(v.source, v.flags)
    } else { // object-Object
      result = new Object()
    }
    // 将拷贝的值与结果存入map
    cache.set(v, result)
    // 对内部自有属性进行递归拷贝
    for(let key in v){
      if(v.hasOwnProperty(key)){
        result[key] = deepClone(v[key])
      }
    }
    return result
  } else {// 基本数据类型
    return v
  }
}
// 测试用例
const a = { 
  number:1, bool:false, str: 'hi', empty1: undefined, empty2: null, 
  array: [
    {name: 'frank', age: 18},
    {name: 'jacky', age: 19}
  ],
  date: new Date(2000,0,1,20,30,0),
  regex: /\.(j|t)sx/i,
  obj: { name:'frank', age: 18},
  f1: (a, b) => a + b,
  f2: function(a, b) { return a + b }
}
// 自引用
a.self = a

const b = deepClone(a)

b.self === b // true
b.self = 'hi'
a.self !== 'hi' //true