扎实基础篇—–浅谈js中的深拷贝

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

前言

学习实现深拷贝之前必备的知识点和浅拷贝相同,没看过的小伙伴可以去这里查看:js的浅拷贝
好,我们继续看。

含义

深拷贝:对于复杂引用数据类型,它会在堆内存中完全开辟一块内存地址,并将原有的对象完全复制过来存放起来。这两个对象是相互独立,不受影响的

- 总结就是:
    - 将一个对象从内存中完整的拷贝出来一份给目标对象。
    - 从堆内存中开辟一个全新的空间存放新对象,并且新对象的修改不会改变原对象。两者真正分离。
复制代码

方法

  • JSON.stringify

    • 理解:就是把一个对象序列化为JSON的字符串,并将对象里面的内容转换成字符串,最后用JSON.parse()将JSON字符串生成一个新对象。
    • 实例
    let obj1 = { a:1, b:[1,2,3] }
    let str = JSON.stringify(obj1);
    let obj2 = JSON.parse(str);
    console.log(obj2);   //{a:1,b:[1,2,3]} 
    
    obj1.a = 2;
    obj1.b.push(4);
    console.log(obj1);   //{a:2,b:[1,2,3,4]}
    console.log(obj2);   //{a:1,b:[1,2,3]}
    
    复制代码
    • 注意点
      • 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;

      • 拷贝 Date 引用类型会变成字符串;

      • 无法拷贝不可枚举的属性;

      • 无法拷贝对象的原型链;

      • 拷贝 RegExp 引用类型会变成空对象;

      • 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;

      • 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。

    • 例子
    function Obj() { 
        this.func = function () { alert(1) }; 
        this.obj = {a:1};
        this.arr = [1,2,3];
        this.und = undefined; 
        this.reg = /123/; 
        this.date = new Date(0); 
        this.NaN = NaN;
        this.infinity = Infinity;
        this.sym = Symbol(1);
    } 
    let obj1 = new Obj();
    console.log('obj=====',obj1)
    let str = JSON.stringify(obj1);
    let obj2 = JSON.parse(str);
    console.log('obj2====',obj2)
    复制代码
  • 递归实现(初级)

    let obj1 = {
        a:{
            b:1
        }
    }
    function deepClone(obj) {
    let cloneObj = {}; 
    for(let key in obj) { // 遍历传入的参数属性值
            if(typeof obj[key] === 'object') { // 如果是对象,就再次调用该函数递归
                cloneObj[key] =  deepClone(obj[key]); 
            }else {
                cloneObj[key] = obj[key]; // 基本类型的话直接赋值
            }
    }
    return cloneObj;
    }
    let obj2 = deepClone(obj1);
    
    复制代码
    • 注意点

      • 这个深拷贝同样也不能复制不可枚举属性和symbol类型;

      • 这种方法只是针对普通的引用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;

      • 对象的属性里面成环,即循环引用没有解决。

  • 递归实现(改进版)

    
        const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null); 
        const deepClone = function(obj,hash = new WeakMap()) {
            if(obj.constructor === Date) {
                return new Date(); 
            }
            if(obj.constructor === RegExp) {
                return new RegExp();
            }
            if(hash.has(obj)) {
                return hash.get(obj);
            }
            let allDesc = Object.getOwnPropertyDescriptors(obj);
            let cloneObj = Object.create(Object.getPrototypeOf(obj),allDesc);
            hash.set(obj,cloneObj);
            for (let key of Reflect.ownKeys(obj)) { 
                cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key];
            }
            return cloneObj;
        }
        let obj = {
            num: 0,
            str: '',
            boolean: true,
            unf: undefined,
            nul: null,
            obj: { name: '芒果啊'},
            arr: [0, 1, 2],
            func: function () { console.log('芒果啊') },
            date: new Date(0),
            reg: new RegExp('/A-Z/ig'),
            [Symbol('芒果')]: '啊',
          };
        Object.defineProperty(obj,'innumerable',{
        enumerable:false,value:'不可枚举属性'}
        )
        obj = Object.create(obj,Object.getOwnPropertyDescriptors(obj));
        obj.loop = obj;
        let cloneObj = deepClone(obj)
        cloneObj.arr.push(4)
        console.log('obj', obj)
        console.log('cloneObj', cloneObj);
    复制代码
    • 知识点:
      • 能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 ** Reflect.ownKeys ** 方法;

      • 参数为Date、RegExp类型,则直接生成一个新的实例返回;

      • 利用 Object的getOwnPropertyDescriptors方法可以获得对象的所有属性,以及对应特性。顺便结合Object的create方法创建一个新对象,并继承传入原对象的原型链。

      • 利用 ** WeakMap ** 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏

总结

其实在我们实际场景中,估计会使用现有的库来去实现深拷贝,比如lodash就很不错,有兴趣的小伙伴可以去了解一番。

前端漫漫长途,我们都在路上,希望可以和小伙伴们一起交流,一起进步。持续更新ing…..

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享