手写深拷贝(递归)

70 阅读3分钟
  1. 初写:使用递归、判断类型(注意看注释内容,写得很仔细清楚)
const deepClone=(a)=>{
    //判断a的类型,是否为对象object
    if(a instanceof Object){
        let result=undefined  //先预设好返回值
        //object
        //再分是哪种类,函数、数组、日期等
        if(a instanceof Function){  //判断是否为函数
            //判断为普通函数还是箭头函数
            if(a.prototype){  //判断是否为普通函数
                result=function(){return a.apply(this,arguments)}  //返回的函数要和a函数功能一模一样(该函数接收什么参数,就一样的传给a(a.apply(this,arguments)),然后将a的返回值作为该函数的返回值,即该函数为a函数的深拷贝)
            }else{  //不是普通函数,就是箭头函数了(仅讨论这两种常见函数)
                result=(...args)=>{return a.call(undefined,...args)}  //同样也是将相同参数传给a,然后将a的返回值作为该函数的返回值
            }
        }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)  //正则的属性source和flags
        }else{  //其他为普通对象
            result={}
        }
        //在此之前的'result='是在构造对应的本体,下方的遍历是内容
        for(let key in a){  //遍历a里所有的属性,然后将所有属性再进行一次深拷贝
            result[key]=deepClone(a[key])  //递归
        }
        return result
    }else{
        //string number bool null undefined symbol bigint
        return a  //普通的数据类型就不用构造了
    }
}
  • 验证:有一个复杂的a对象,然后b深拷贝a
const a={
    number:1,bool:false,str:'hi',empty1:undefined,empty2:null,
    array:[
        {name:'frank',age:18},
        {name:'jack',age:19}
    ],
    date:new Date(2024-06-03),
    regexp:/\.(j|t)sx/i,
    obj:{name:'frank',age:18},
    f1:(a,b)=>a+b,
    f2:function(a,b){return a+b}
}
const b=deepClone(a)
  • 深拷贝成功,a和b互不影响 微信图片_20240603222634.png
  1. 检查环
  • 当加上'a.self=a'(自己引用自己,即指向当前a对象的引用)后,递归没有出口 微信图片_20240604111531.jpg 微信图片_20240604110523.png 微信图片_20240604110805.png
  • 如何解决该环:用一个Map记录,当要再次深拷贝a时,直接return b即可,如图 微信图片_20240604121428.jpg
const cache=new Map()  //为什么用Map,因为key是对象
const deepClone=(a)=>{
    //判断是否已经深拷贝过了,即看缓存cache里有没有记录
    if(cache.get(a)){
        return cache.get(a)  //若有,直接return a(即拒绝该深拷贝) ,其实是b哦,因为已进行过深拷贝了
    }
    if(a instanceof Object){
        let result=undefined  
        if(a instanceof Function){  
            if(a.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={}
        }
        //result构造出来后,将深拷贝记录进缓存里
        cache.set(a,result)
        for(let key in a){ 
            result[key]=deepClone(a[key])  
        }
        return result
    }else{
        return a  
    }
}

微信图片_20240604122943.png 3. 注意:不要拷贝原型上的属性:有些属性是继承得到的,就不需要深拷贝到b身上(因为这些属性就不在a对象身上),所以要进行判断

for(let key in a){
  if(a.hasOwnProperty(key)){  //若该key是在a身上,才会去拷贝,在原型上就不用拷贝
    result[key]=deepClone(a[key])
  }
}