从来都没有理解浅拷贝与深拷贝?我非把你教会不可!最全的深拷贝方案,绝对全!!看这一篇就够了大白话!

文章详细介绍了JavaScript中深拷贝和浅拷贝的概念,强调了它们与堆栈内存、简单数据类型和引用数据类型的关系。通过实例展示了浅拷贝如何导致对象间的相互影响,并提供了多种实现深拷贝的方法,包括手动遍历、JSON序列化、自定义函数以及使用lodash库。同时,文章提到了深拷贝的限制,如函数丢失和循环引用问题,并给出了解决方案。
摘要由CSDN通过智能技术生成

首先,想真正理解深浅拷贝,需要清楚地知道堆和栈,那么你知道简单数据类型和引用数据类型在堆和栈的关系吗?

栈:基本数据类型的值,引用数据类型的地址。

堆:引用数据类型的值。

我画个图,便于你理解。堆空间远远大于栈空间,可以发现,当数据类型为简单数据类型时,它的值和地址都在中,所以一般对于深拷贝浅拷贝都相对于引用数据类型来说的,如果严格区分基本类型的复制,也可以说属于深拷贝。所以,可以说无论哪种数据类型,存储其值所声明的变量都存在于栈空间!

引用数据类型在使用“=”时,赋值的是栈空间中的地址。

注意:尽量避免使用“=”对引用数据类型进行拷贝;修改一个对象,也会影响另一个对象。

浅拷贝对于对象和数组引用数据类型赋值时,只是把地址复制过去,一个值变化也会影响另一个值变化。(看下面图片再来看看这句话哦!!)

上例子var obj = {

            uname: "张三",

            age: "18"

  }

  var obj2 = obj;

        obj2.uname = "李四"

        console.log("obj:", obj);

        console.log("obj2:", obj2);

深拷贝:对于对象和数组引用数据类型赋值时,如果我们把值赋过去(产生了一个新的值),此时,不影响另一个值变化

上例子:var obj = {

            uname: "张三",

            age: "18",

            address: "北京",

            aiHao: ["吃", "喝"],

            car: {

            }

        }

var obj2 = {};

        for (var key in obj) {

            console.log("key:", key); // 属性名

            console.log(obj[key]); // 属性值

            // 对象[属性名] = 属性值

            obj2[key] = obj[key]

            // obj2['aiHao'] = ["吃", "喝"]

        }

        obj.age = 20

        obj.aiHao.push("玩")

        console.log("obj:", obj);

        console.log("obj2:", obj2);

这里我用了for...in 通过遍历对象并声明obj2(空对象)来盛装对象中每一个属性,先别急,我知道还有其他方法,我后面会做总结喔!!!这里只是抛砖引玉,你会发现上面例子只能实现含有一层引用数据类型的对象,那么对象里面可能还有多层嵌套(对象中还有包含多个数组/对象的数组/对象),又该怎么办呢?

上例子: var obj = {

            uname: "张三",

            age: "18",

            address: "北京",

            aiHao: ["吃", "喝", []],

            car: {

                jiaGe: "100",

                pinPai: "bmw"

            },

             say: function () { }

        }    

var objStr = JSON.stringify(obj)

        var obj2Str = objStr

        var obj2 = JSON.parse(obj2Str);

        obj.uname = "李四";

        obj.aiHao.push("玩");

        console.log("obj:", obj);

        console.log("obj2:", obj2);   //fn被忽略

这里是通过json方法实现的深拷贝,

但是!!!有两种情况会出现bug:第一种情况,使用json方法实现深拷贝不能拷贝函数,因为当拷贝的数据中有函数类型,会自动忽略,打印时候发现根本没有拷贝到fn;第二种情况如下,牵扯到循环引用(什么是循环引用,我在下面打印一下,你就知道了)

下面是循环引用的样子:

那么有没有很完美的深拷贝呢,我先写一种自己封装的深拷贝函数,这种比较好理解,后面还有另一种,往下找哦!!!:

 

最后,我来总结一下我自己总结的全部的深拷贝方案以及优缺点:

第一种:

深拷贝的第一种方案展开运算符实现 特点:能实现一层拷贝,如果有多层,实现不了。

第二种:

深拷贝的第二种方案上面我提到的json实现 ,能实现多层拷贝,如果对象中有方法,会把对象中的方法丢掉而且不能实现循环引用,会报错,详细见上文哦!!!。

第三种(不正规)

深拷贝的第种方案通过原型实现的,表现像深拷贝,从内存的角度分析是原型实现,深层次的也实现不了但是效果确实实现了。

 

第四种:

自己手写封装深拷贝看上图,有详细解释哦!!!

第五种:

使用第三方的库lodash可以去官网找实现

 第六种:

这个也是自己封装的,但是比上面一种简练,方法不一样。

function shen(a) {

            var x = a instanceof Array ? [] : {}

            for (var i in a) {

                x[i] = a[i] instanceof Object ? arguments.callee(a[i]) : a[i]

            }

            return x

        }

        var obj3 = shen(obj)

        obj3.name = "赵六"

        obj3.data.msg = '失败'

        console.log('obj3:', obj3);  // 深拷贝,完美复刻,任意修改,完全不影响原数据

第七种:

这种牵扯到底层原理,小伙伴们研究看看哦!!!

 function structuralClone(obj) {

            return new Promise(resolve => {

                const { port1, port2 } = new MessageChannel()

                port1.postMessage(obj)

                port2.onmessage = e => resolve(e.data)  // e是MessageChannel构造函数本身

            })

        }

        obj.data.d = obj.data

        // 注意该方法是异步的 // 可以处理 undefined 和循环引用对象

        const test = async () => {

            const obj5 = await structuralClone(obj)

            console.log('obj5',obj5)

        }

        test() 

整理不易,望小伙伴们多多指教!!!

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值