原生js实现深拷贝

8,095 阅读3分钟

手写实现一个深拷贝是面试题经常会考到的,但是很多人可能考虑不全面,今天我们就来逐步实现一个考虑全面的深拷贝.

深拷贝和浅拷贝的区别?

浅拷贝:将内存中的某个对象复制一份,在内存中开辟一块新的空间,如果复制的这个对象的属性为基本数据类型        
,则拷贝的便为这个值本身,如果为复杂数据类型,则拷贝复制的为地址,因此,修改新对象会对原对象产
生影响

深拷贝:开辟一块新的空间,完整的复制一份,包括复杂数据类型,拷贝的这个对象和原对象无任何关系,修改什么 的都互不影响

先从浅拷贝入手(基础版本)

简单思路:1.创建一个新对象
         2.遍历要克隆的对象
         3.给新对象添加上对应的成员
    function qianClone(obj){
        var newObj={};
        for (var key in obj){
            newObj[key]=obj[key]
        }
        return newObj;
    }
    
    var xm={
        name:"xm",
        age:30,
        cars:{
            bmw:"30w",
            bench:"60w"
        }
    }
    
    var newXm=qianClone(xm);
    console.log(newXm)

显然实现了拷贝功能. 如果对拷贝后的对象进行修改呢?

     newXm.name="Dm";
     newXm.cars.bmw="40w";
     console.log(newXm)

结果:

但是如果打印一下xm对象就会发现,xm对象的cars属性也会被改掉

于是我们可以想到如何可以进行深层拷贝,将对象内部的对象也能完整的拷贝一份;

深拷贝

思考:如何简单修改浅拷贝的代码即可实现深拷贝? 修改浅拷贝的代码

       function deepClone(obj) {
        //如果不是复杂数据类型,就直接返回一个一样的对象
        if(typeof obj !="object"){
            return obj
        }
        //如果是,就递归调用
        var newObj = {};
        for (var key in obj) {
          newObj[key] = deepClone(obj[key])
        }
        return newObj;
      }
      
      var newXm = deepClone(xm);

      newXm.name = "Dm";
      newXm.cars.bmw = "40w";
      console.log(newXm);
      console.log(xm)

显然需求已经实现,但是需不需要优化?

优化版本深拷贝

先看函数代码

 function deepClone(obj) {
        //如果不是复杂数据类型,就直接返回一个一样的对象
        if(typeof obj !="object"){
            return obj
        }
        //如果是,就递归调用
        var newObj = {};
        for (var key in obj) {
          newObj[key] = deepClone(obj[key])
        }
        return newObj;
      }

用几个常用数据测试一下上面函数

1.null

    var o=deepClone(null)
    console.log(o)

打印结果:

2.正则表达式

    var RegExp=/[\u4e00-\u9fa5]/gm;
    var o=deepClone(RegExp)
    console.log(o)

打印结果:

3.日期对象

    var RegExp=/[\u4e00-\u9fa5]/gm;
    var o=deepClone(RegExp)
    console.log(o)

打印结果:

4.数组对象

    var arr=["牛爷爷","大头儿子","围裙妈妈"]
    //不要管我为什么把他们仨放一组enmm
    var o=deepClone(arr)
    console.log(o)

打印结果:

显然结果均是错误的,因此,如何解决我们的deepClone的bug嘞?

解完bug代码:

    function deepClone(obj) {
      if (obj === null) return null; //null 的情况
      if (obj instanceof RegExp) return new RegExp(obj); //正则表达式的情况
      if (obj instanceof Date) return new Date(obj); //日期对象的情况
      if (typeof obj == 'Function') return new function(obj){}; //函数的情况
      if (typeof obj != "object") {
        //非复杂类型,直接返回 也是结束递归的条件
        return obj
      }
      //[].__proto__.constructor=Array()
      //{}.__proto__.constructor=Object()
      //因此处理数组的情况时,可以取巧用这个办法来new新对象
      var newObj = new obj.__proto__.constructor;
      for (var key in obj) {
        newObj[key] = deepClone(obj[key])
      }
      return newObj;
    }
//测试代码
var obj={
      name:'xm',
      birth:new Date,
      desc:null,
      reg:/^123$/,
      ss:[1,2,3],
      fn:function(){
        console.log('123')
      }
    }

    var obj2 = deepClone(obj);
    console.log(obj,obj2)

    obj.fn()
    obj2.fn()

输出值:

补充说明:(以下也是面试常考)

1.typeof 和 instanceof的区别?
    typeof可以判断出基本数据类型(当然除了null的数据类型为object的bug),还可以正确判断出某个对象是否为function,其余的Date,Array等无法判断
    instanceof相反,可以准确判断出复杂数据类型,但是无法判断简单数据类型.
2.实例对象的原型链

看完不点赞的太过分了!!!!!!