面试复习题-手写深拷贝

75 阅读3分钟

✊不积跬步,无以至千里;不积小流,无以成江海

什么是深拷贝

不是浅拷贝的都是深拷贝。

将一个对象的a属性集合,完全拷贝成b,使a/b完全一致,且ab的属性之间没有任何的交集,引用b的属性不会指向a,就是深拷贝。

/////

浅拷贝是指只复制对象的引用,而不是复制对象本身。也就是说,原对象和拷贝对象指向同一块内存空间

方法一,用 JSON:

思路

用json方法可以实现深拷贝,但是有如下问题:

  1. 不支持 Date、正则、undefined、函数等数据

  2. 不支持引用(即环状结构)

实现

//             反序列化     序列化
const b = JSON.parse(JSON.stringify(a))

方法二,用递归:

思路

自己调用自己。(第2行和第30行)

  1. 先判断数据的类型,属于八种中的哪一个,由于对象最特殊,则分为对象 & 其他类型
  2. 在对象中,分为函数,数组,日期和正则。
  3. 在函数中,需要判断是普通函数还是箭头函数
  4. 如果该函数有protopype,那么它就是普通函数,我们需要返回一个结果(结果需提前声明),能够复制a的所有功能。则实现该功能需要apply a传入的所有参数,并返回。
  5. 那么对于函数的所有属性,对a进行遍历并复制给deepClone函数
  6. 如果该函数为箭头函数,那么将这个函数传入的所有属性都传给a
  7. 在对象中,如果该类型属于数组,则将return赋值给数组
  8. 在对象中,如果该类型属于日期,则将return赋值给newData,newData - 0 是日期序列化之后的结果
  9. 在对象中,如果该类型属于正则,则将return赋值给newRegExp,newRegExp 接受两个参数,文本source和flags
  10. 则在对象中,剩余的情况,就默认为是普通属性直接拷贝。
  11. 设置一个a去测试一下,发现如果a.self时(自引用)会出错(需要解决环),为了解决这个问题,我们可以给a添加一个“记忆功能”,如果a第二次次访问的地址之前已经访问过了,就直接导到第一次的地址。
  12. 那么这个记忆功能需要一个map来去解决,用cache命名。因为map的key是对象,所以用map属性。(obj的key是string / symbol)
  13. 如果我们get一下cache发现有a,我们就返回对应被我们拷贝的东西。并用cache.set将缓存a和result做对应
  14. 当我们拷贝时,存在一种情况,当它的有些属性是继承自其他函数时,我们不应该拷贝它。所以在for循环遍历里面,加上限定条件,当key为自身属性时,我们才拷贝它。

实现

const cache = new Map()
const deepClone = (a) => {
if(cache.get(a)){
    return cache.get(a)
} 
if(a instanceof Object) {
    let result
    if(a instanceof Function) {
        if(a.prototype) { // 有 prototype 就是普通函数
            result = function(){ return a.apply(this, arguments) }

        } else {
            result = (...args) => { return a.call(undefined, ...args) }
        }
    } else if(a instanceof Array) {
            result = []
    } else if(a instanceof Date) {
            result = new Date(a - 0)
    } else if(a instanceof RegExp) {
            result = new RegExp(a.source, a.flags)
    } else {
            result = {}
    }
    
    cache.set(a, result)
    
    
    for(let key in a) {
    if(a.hasOwnProperty(key)){
        result[key] = deepClone(a[key])
    }}

}

else{
    // number symbol string bool null undefine bigint
    return a
}
}



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