浅谈深度clone的实现

159 阅读2分钟

写作背景

项目中基本有写好的第三方库供我们调用,那么为什么我这里还要书写这个呢 1: 这种东西可以提升我们书写代码的能力,以及思考问题和解决问题的思想

正式编写

何为深度clone ?

就是在原来对象的基础上复制一模一样的东西,clone 之后,原对象的变化不会应用到clone对象中

先来实现一个浅clone

function deepClone(source){
    let target ={};
    for (const key in source) {
      if (Object.hasOwnProperty.call(source, key)) {
        target[key] =  source[key]
      }
    }
    return target
}
    

相信这个大家基本写的出来。接下来我们开始分析

1:没有对传进来的source进行分析,可能为null,可能是数组

解决方案

function deepClone(source){
     // 判断为null
     if(source===null){
       return null
     }
     // 判断数组还是对象
     let target =Array.isArray(source)?[]:{};

     for (const key in source) {
       if (Object.hasOwnProperty.call(source, key)) {
         target[key] =  source[key]
       }
     }
     return target
 }
  

2: 这里还会存在一个问题就是如果对象的属性是Symbol 那么使用forin 是无法遍历出Symbol类型的,这里的解决方案是既然是es6新增属性,那么可以利用es6的ReflectApi 进行解决

    function deepClone(source){
      // 判断为null
      if(source===null){
        return null
      }

      // 判断数组还是对象
      let target =Array.isArray(source)?[]:{};

      // 利用ReflectApi 可以遍历出Symbol属性 
      Reflect.ownKeys(source).forEach(key=>{
        target[key] =  source[key]
      })

      return target
  }

3 如何解决深度clone?既需要判断原对象该属性是引用还是原始值(函数是应用值但是copy 可以直接赋值)

解决方案

function deepClone(source){
      // 判断为null
      if(source===null){
        return null
      }
      // 判断数组还是对象
      let target =Array.isArray(source)?[]:{};
      // 利用ReflectApi 可以遍历出Symbol属性 
      Reflect.ownKeys(source).forEach(key=>{
        const src = source[key];
        //这里不用判断null特殊情况因为之前的判断过
        if(typeof src === "object"){
          target[key] = deepClone(src)
        }else{
          target[key] = src
        }
      })
      return target
  }

4:以上基本实现了深度clone的方法,但是在某些特殊情况还是会存在问题,比如

  在引用循环的时候
  
  const obj = {
       name:"hello",
       jk:{
         name:"jk"
       }
    }
    obj.lk ={
       a:"a",
       b: obj.jk
    }
 
   于是想到用缓存 这里使用数组实现(weakMap 也行)
   
   function deepClone(source,cacheArr=[]){
        // 判断为null
        if(source===null){
          return null
        }
        const findItem = cacheArr.find(item=>item.source ===source)
        if(findItem){
          return findItem.target
        }
        // 判断数组还是对象
        let target =Array.isArray(source)?[]:{};
        // 利用ReflectApi 可以遍历出Symbol属性 
        Reflect.ownKeys(source).forEach(key=>{
          const src = source[key];
          //这里不用判断null特殊情况因为之前的判断过
          if(typeof src === "object"){
            target[key] = deepClone(src,cacheArr)
          }else{
            target[key] = src
          }
          cacheArr.push({
            source:src,
            target:target[key]
          })
        })
        return target
    }
    

5:以上基本实现了深度clone的方法,这样是不是就是最终版了呢?答案依然是不是的,因为在数据足够多的情况下会存在爆栈?怎么解决。我不想解决,因为使用树形遍历又会带来下一个问题,无穷无尽,目前我只能学习到这里,也许后面会补充这个。