美文网首页
深拷贝和浅拷贝

深拷贝和浅拷贝

作者: 呆桃冲鸭冲鸭 | 来源:发表于2021-03-02 08:55 被阅读0次

    浅拷贝是自己创建一个新的对象,来接受你要重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响到另一个对象。

    深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

    image.png

    浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

    浅拷贝:
    方法一:object.assign
    方法二:扩展运算符方式
    方法三:concat 拷贝数组
    方法四:slice 拷贝数组
    方法五:函数库lodash的_.clone方法

    赋值和深/浅拷贝的区别
    这三者的区别如下,不过比较的前提都是针对引用类型:

    赋值:当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

    浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

    深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。


    image.png

    实现浅拷贝,大致的思路分为两点:
    1.对基础类型做一个最基本的一个拷贝;
    2.对引用类型开辟一个新的存储,并且拷贝一层对象属性。

    //手动实现浅拷贝
    // 1.对基础类型做一个最基本的一个拷贝;
    // 2.对引用类型开辟一个新的存储,并且拷贝一层对象属性。
    function cloneFn(val) {  
        if(typeof val === "object" && typeof val !== null){
            // 引用类型
            const target = Array.isArray(val) ? [] : {}
            for(var key in val){
                if(val.hasOwnProperty(key)){
                    target[key] = val[key]
                }
            }
            return target
        }else{
            //基础类型
            return target
        }
    }
    console.log(cloneFn({
        name : '浪里行舟',
        arr : [1,[2,3],4],
    }))
    

    浅拷贝只是创建了一个新的对象,复制了原有对象的基本类型的值,而引用数据类型只拷贝了一层属性,再深层的还是无法进行拷贝。深拷贝则不同,对于复杂引用数据类型,其在堆内存中完全开辟了一块内存地址,并将原有的对象完全复制过来存放。
    这两个对象是相互独立、不受影响的,彻底实现了内存上的分离。

    深拷贝的原理:
    将一个对象从内存中完整地拷贝出来一份给目标对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。

    深拷贝:
    方法一:JSON.stringfy
    把一个对象序列化成为 JSON 的字符串,并将对象里面的内容转换成字符串,最后再用 JSON.parse() 的方法将JSON 字符串生成一个新的对象。
    使用 JSON.stringfy 实现深拷贝的注意点:
    拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
    拷贝 Date 引用类型会变成字符串;
    无法拷贝不可枚举的属性;
    无法拷贝对象的原型链;
    拷贝 RegExp 引用类型会变成空对象;
    对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
    无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。

    方法二:手写递归实现

    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);
    obj1.a.b = 2;
    console.log(obj2);   //  {a:{b:1}}
    

    存在问题:
    1.这个深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型;
    2.这种方法只是针对普通的引用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
    3.对象的属性里面成环,即循环引用没有解决。

    方法三:改进后递归实现

    相关文章

      网友评论

          本文标题:深拷贝和浅拷贝

          本文链接:https://www.haomeiwen.com/subject/vuhafltx.html