手写支持函数、日期和正则的深拷贝

401 阅读3分钟

使用const data = JSON.parse(JSON.stringify(x))完成的深拷贝

可解决大部分情况下深拷贝需求,但不支持Date、正则、undefined、函数等数据,且不支持引用。

所以此时需要一个更完备的深拷贝方法。

先上最终代码


    const cache = new Map()
    function deepClone(obj){
      
      if(typeof obj !== 'object' || obj === null) return obj;

      if(cache.get(obj)){
        return cache.get(obj)
      }

      let result = {}
      
      if(obj instanceof Function){ // 不能100%拷贝
        if(obj.prototype){
            result = function(){ 
                return obj.apply(this,arguments)
            }
        }else{
           result = (...args) => a.call(undefined, ...args)
        }
      }

      if(obj instanceof Array){ 
        result = []
      }

      if(obj instanceof Date){ 
        result = new Date(obj - 0)
      }

      if(obj instanceof RegExp){ 
        result = new RegExp(obj.source, obj.flags)
      }
      
      cache.set(obj, result)
      for(let key in obj){
        result[key] = deepClone(obj[key])
      }

      return result
    }
    
    // 测试数据
    const obj1 = {
      number:1, bool:false, str: 'hi', empty1: undefined, empty2: null,
      array: [
      {name: 'hy', 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 }
    }
    obj1.self = obj1
    const obj2 = deepClone(obj1)
    obj1.array[0] = 'xxx'
    
    

基本实现


// 创建函数
function deepClone(obj){
    // 创建返回值变量
    let result = {}
    
    return result
}

function deepClone(obj){
    /* 其他代码 */
    
    //传参不为对象 直接return
    if(typeof obj !== 'object' || obj === null) return obj;
    
    /* 其他代码 */
}

function deepClone(obj){
    /* 其他代码 */
    
    // 传参为函数
    if(obj instanceof Function){ // 不能100%拷贝
        //普通函数
        if(obj.prototype){
            result = function(){
                return obj.apply(this,arguments)
            }
        }else{
           //箭头函数
           result = (...args) => a.call(undefined, ...args)
        }
    }
    
    /* 其他代码 */
}

function deepClone(obj){
    /* 其他代码 */
    
    // 传参为数组
    if(obj instanceof Array){ 
      result = []
    }
    
    /* 其他代码 */
}

function deepClone(obj){
    /* 其他代码 */
    
    // 传参为日期
    if(obj instanceof Date){ 
      // obj为日期时,则格式如 -> Sun Jun 19 2022 20:03:03 GMT+0800 (中国标准时间)
      // obj - 0 则得到此时间戳 -> 1655640152585
      // 再将此时间戳赋予到new Date中
      result = new Date(obj - 0)
    }
    
    /* 其他代码 */
}

function deepClone(obj){
    /* 其他代码 */
    
    // 传参为正则
    if(obj instanceof RegExp){ 
        result = new RegExp(obj.source, obj.flags)
    }
    
    /* 其他代码 */
}

function deepClone(obj){
    /* 其他代码 */
    
    // 基本赋值
    for(let key in obj){
        // 有些属性是继承,有些属性是自身,我们只遍历自身属性
        if(obj.hasOwnProperty(key)){
        
            // 直接赋值是不行的,因为obj[key]也有可能是对象,直接赋值就变成了浅拷贝
            // result[key] = obj[key] 

            // 正确写法,赋值前调用深拷贝方法,如果是普通类型则不变,如果为对象类型则进行深拷贝。
            result[key] = deepClone(obj[key])
        }
    }
    
    /* 其他代码 */
}

基本完成


 function deepClone(obj){
      if(typeof obj !== 'object' || obj === null) return obj;

      let result = {}
      
      if(obj instanceof Function){ // 不能100%拷贝
        if(obj.prototype){
            result = function(){
                return obj.apply(this,arguments)
            }
        }else{
           result = (...args) => a.call(undefined, ...args)
        }
      }

      if(obj instanceof Array){ 
        result = []
      }

      if(obj instanceof Date){ 
        result = new Date(obj - 0)
      }

      if(obj instanceof RegExp){ 
        result = new RegExp(obj.source, obj.flags)
      }
      
      for(let key in obj){
         if(obj.hasOwnProperty(key)){
            result[key] = deepClone(obj[key])
         }
      }

      return result
    }
    
    // 测试数据
    const obj1 = {
      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 }
      }
    const obj2 = deepClone(obj1)
    obj1.number = 2

解决环状问题

之前已经可以满足普通的深拷贝需要了,但此时还有个问题

那就是在深拷贝前,如果变量obj1的self等于自身,也就是说先做了obj1.self = obj1的操作。

之后我们再运行const obj2 = deepClone(obj1)这行代码,则会报错..

image.png

image.png

如何解决...

// 第一步
const cache = new Map()
function deepClone(obj){
    /* 其他代码 */
    
    // 第二步
    if(cache.get(obj)){
        return cache.get(obj)
    }
     /* 其他代码 */
    
    // 第三步
    cache.set(a, result)
    
    for(let key in obj){
      if(obj.hasOwnProperty(key)){
        result[key] = deepClone(obj[key])
      }
    }
    /* 其他代码 */
}

此时再运行obj1.self = obj1 + const obj2 = deepClone(obj1),就不会再报错啦

image.png