深拷贝的几种实现,以及注意的点。

一直以来,都在用深拷贝。但从没自己实现过,想着是挺简单的,其实动手做还是有点坑的。

概念:

    普通的赋值,如果是引用类型(object)的话,如果把a赋值给b,那么我改变b,a也会变化,这个是浅拷贝。深拷贝则是与之相反,赋值与被赋值的怎样改变,也不会影响彼此。 这也是我们大多数时候想要的。

原理:

    之所以会出现深浅拷贝的情况,是因为普通赋值如果是引用类型,那么js并不会在堆栈中新开辟空间来存储,而是仅仅将其指向堆栈中数据,这样本质上其实最终无论操作被赋值或者赋值的变量,其实都相当于操作堆栈中的同一个数据;

对应的,js遇到基础类型String、Boolean、Undefined、Null、Number、Symbol),或其他非引用类型则会在堆栈中新创建空间来储存,所以基础类型不存在深浅拷贝的说法,也可以将此认为是实现深拷贝的基本(个人理解)

实现方法:

先把可实现方法列出来:assign,递归全赋值,JSON.parse(JSON.stringify()),function return

首先说一说不该叫深拷贝,但确实又不必担心深浅拷贝问题的 —— function return

先看一串代码:

            function getObj() {
                return {
                    a:{
                        b:{
                          c:1
                        }
                    }
                }
            }
            let obj = getObj()
            let obj2 = getObj()
            obj2.a.b.c=6
            console.log(obj.a.b.c) // 打印1

结果上,obj与obj2互不影响,那么确实是深拷贝效果,但是此操作不大能算是拷贝,所以我们改变下代码,如下:

            let o = {
                    a:{
                        b:{
                          c:1
                        }
                    }
                }
            function getObj() {
                return o
            }
            let obj = getObj()
            let obj2 = getObj()
            obj2.a.b.c=6
            console.log(obj.a.b.c) // 打印6

我们发现,并未达到我们的预期,也就是说,此方法想达到深拷贝的效果只能是预先写好的,所以我们想拿到一个变量后通过此方法将其深拷贝是不可行的,这也是我为什么说他不算是拷贝。当然,项目中写一写初始信息,配置信息类的用起来是非常好用的。

关于原理,其实看一串代码要比打字说明理解快:

            function getObj(){
                return {
                    a:1
                }
            }
            let o1 = getObj()
            let o2 = getObj()
            // 等价于
            let o1 = {
                a:1
            }
            let o2 = {
                a:1
            }

通过return一个变量不可以深拷贝,也是同理,因为相当于把此变量反复赋值。

号称乞丐版,但也最简单最实用,并且坑最多的深拷贝 —— JSON.parse(JSON.stringify())

    上面提到过,js遇到基础类型会在堆栈中新建空间来储存,也就是说所有基础类型进行赋值都是深拷贝,这也正是JSON.parse(JSON.stringify())能实现深拷贝的原理,在JSON.stringify()的时候,就已经变为了String并且和被赋值变量脱离关系,再将String转为JSON后则完全是一个新的数据了。因为其中数据变为了String,所以则不会有引用关系(首尾呼应,基础类型全民轻易深拷贝),所以实现了深拷贝。

    这个方法的坑:可以这么说,这个方法多简单,他的坑就有多多! 实践中除非需要深拷贝的数据为最基础最基础的json数据,不然别用。简单罗列下会遇到的问题:构造函数丢失,function丢失,Undefined丢失、正则表达式变为{}、Data变成String、Set类型变Array,Map类型变Array。由于太多,可能还有一些没想起来的。。。

虽然,坑这么多。但我们数据大多时候并没这么复杂,所以说——真香!

可以实现完美的深拷贝—— assign,递归全赋值

这个没什么特别说的,遇到深度为一的对象,assign直接可以实现深拷贝,多维的就是循环递归,将所有的都assign掉,这个与全赋值的写法差不多,下面直接贴下全赋值的深拷贝方法:

function deepClone(obj) {
            let clone = {}
            if(obj instanceof RegExp) return new RegExp(obj);
            if(obj instanceof Date) return new Date(obj);
            if(obj === null) return null;
            if(typeof obj !== 'object') return obj;
            if (Array.isArray(obj)) {
                clone = []; // 将result赋值为一个数组,并且执行遍历
                for (let i in obj) {
                    // 递归克隆数组中的每一项
                    clone.push(deepClone(obj[i]))
                }
                // 判断如果当前的值是对象,那么逐个赋值,并对各个值深拷贝
            } else {
                for(let key in obj) {
                    // 此操作虽然是简单的赋值,但却涵盖了一切可能,无论被拷贝对象是什么类型都不会丢失构造函数等
                    clone[key]=obj[key]
                    // 继续深入拷贝
                    deepClone(clone[key])
                }
            }

            return clone;
        }

此方法还缺少了对Set、Map类型的判断,如需要可进行判断并处理;可以说是很多类型需要单独判断是这个方法唯一的坑了,当有问题时候检查下是不是对应类型没特殊处理准没错。

总结: 其实我们有发现,无论是哪种深拷贝方法,数据都是止于非引用类型,然后实现了深拷贝(递归赋值最终也要进行到非引用类型才不继续递归),所以说非引用类型的特性才是我们能实现深拷贝的根本(首尾呼应,个人理解,毕竟非引用类型没指针问题)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值