摘要:对象拷贝,简单来说就是把对象再拷贝一遍,但是不同的拷贝方式会得到不同的结果。本文分享自华为云社区《js对象深浅拷贝,来,试试看!》,作者:北极光之夜。.1、快速知识的概念:对象复制,简单来说就是把对象再复制一份,但是不同的复制方式会产生不同的结果。比如直接给new变量赋一个对象://1.创建一个对象varobj={name:"NightoftheNorthernLights.",like:"aurora",};//2.直接将对象赋值给变量clonevarclone=obj;//3.修改obj的like属性obj.like="wind";//4.输出克隆对象console.log(clone);从输出结果可以看出,我明明改变了obj对象的属性,但是clone对象的属性也发生了变化。这是因为,obj对象在创建时,会在堆内存中开辟一块空间来存放对象的内容。而clone直接给obj赋值的时候,clone并不会开辟新的堆内存,而是obj告诉clone,我给你我内存空间中存放的对象的地址。该地址存储在堆栈内存中。你通过栈内存的地址在堆内存中找到对象的内容,我们共享一下就完事了。因此,obj和clone都指向同一块内容。无论谁更改了对象的内容,其他人的访问都是更改后的。所以这不是我们想要的,我不想分享,我想要一个自己的世界,我帮不了你,所以这个需要浅拷贝和深拷贝。简单补充:当一些基本数据类型的变量(NumberBooleanStringundefinednull)被赋值时,会直接在栈内存中开辟一个新的存储区来存放新的变量,而不是直接给别人引用。2、浅拷贝原理及常用方法:浅拷贝简单来说就是只拷贝一层。这意味着什么?例如,我有一个对象obj:varobj={name:"NightoftheNorthernLights.",like:"aurora",};我想把它复制到变量b中,原理是我会开辟一块新的内存,然后直接复制一份obj中的属性和值例如,通过以下方式://1.创建对象varobj={name:"NightoftheNorthernLights.",like:"aurora",};//2.封装一个函数传入一个对象并返回复制的新对象functioncloneObj(obj){letclone={};//3.使用forin遍历属性ofobjfor(letiinobj){clone[i]=obj[i];}returnclone;}//4.执行函数并获得一个新对象varclone=cloneObj(obj);//5.改变obj属性值obj.like="wind";//6.输出console.log(clone);结果:可以看到是新建一个空对象,或者直接循环赋值给它,然后改变obj的like属性值,新创建的对象不会受到影响。但是如果obj是这样的形式呢:varobj={name:"NightoftheNorthernLights.",like:"aurora",num:{a:"1",b:"2",},};这时候上面的方法就不行了。如果obj只是改变属性像名字问题,但是当obj改成num这种引用类型的数据(对象和数组都是引用类型),复制出来的对象还是可以受影响的,因为浅拷贝只能复制一层,如果还有子对象的话,子对象的copy只是为了得到一个地址,这个从上面的代码也可以看出来,只是一层循环而已,想要真正达到我的缘分,就得用深拷贝,真正get到它的底部。深拷贝见第三点。下面介绍浅拷贝的常用方法。当对象只有一层时,最好使用浅拷贝。浅拷贝的常用方法:1、第一种方法是使用forin遍历原对象的属性。//封装一个函数,传入一个对象,返回一个复制的新对象functioncloneObj(obj){letclone={};//使用forin遍历obj的属性for(letiinobj){clone[i]=obj[i];}返回克隆;}2.您可以使用Object.keys()方法:Object.keys()方法将返回一个由给定对象自身的可枚举属性组成的数组。函数cloneObj(obj){让克隆={};for(letiofObject.keys(obj)){clone[i]=obj[i];}返回克隆;}3.可以使用Object.entries()方法:Object.entries()方法返回给定对象自身可枚举属性的键值对数组。函数cloneObj(obj){让克隆={};for(let[key,value]ofObject.entries(obj)){clone[key]=value;}返回克隆;}4.可用于Object.getOwnPropertyNames()forEach循环:Object.getOwnPropertyNames()返回其属性的数组。函数cloneObj(obj){让克隆={};Object.getOwnPropertyNames(obj).forEach(function(item){clone[item]=obj[item];});返回克隆;}5.可用的Object.defineProperty()方法:Object.defineProperty(obj,prop,descriptor)方法会直接在一个对象上定义一个新的属性,或者修改一个对象已有的属性,并返回这个对象。obj要定义其属性的对象。prop要定义或修改的属性的名称或符号。descriptor要定义或修改的属性描述符。Object.getOwnPropertyDescriptor():返回指定对象自身属性对应的属性描述符。属性描述符:JS提供了一种内部数据结构,用于描述对象的值并控制其行为。称为属性描述符。函数cloneObj(obj){让克隆={};Object.getOwnPropertyNames(obj).forEach(function(item){//获取原对象的每个属性修饰符vardes=Object.getOwnPropertyDescriptor(obj,item);//将属性修饰符赋值给新对象Object.defineProperty(克隆,项目,des);});返回克隆;其他的方法还有很多,我就不一一列举了。3、深拷贝的常用方法:深拷贝不会像Shallowcopy只拷贝一层,而是我有多少层就拷贝多少层,让所有的内容真正的放在新开的内存中。深拷贝可以利用递归的思想来实现。1.可以这样实现,也可以用forin循环,如果是属性对象就会递归:functioncloneObj(obj){letclone={};for(letiinobj){//如果是对象,则递归一层复制if(typeofobj[i]=="object"&&obj[i]!=null){clone[i]=cloneObj(obj[i]);}else{克隆[i]=obj[i];}}返回克隆;}试试看://1.创建一个对象varobj={name:"NightoftheNorthernLights.",like:"aurora",age:{a:1,b:2,},};//2.封装一个函数,传入一个对象,返回一个复制的新对象functioncloneObj(obj){letclone={};for(letiinobj){//如果是对象,递归再往上一层复制if(typeofobj[i]=="object"&&obj[i]!=null){clone[i]=cloneObj(obj[i]);}else{clone[i]=obj[i];}}returnclone;}//4.执行函数并得到一个新对象varclone=cloneObj(obj);//5.改变obj属性值obj.age.a="666";//6.输出console.log(clone);结果如下,复制成功,原对象无法改变新的object:2.如果object里面有数组怎么办?数组和??对象一样也是引用类型,所以我们可以在开头加一行判断是对象还是数组。如果是数组,赋一个空数组,同理遍历副本:functioncloneObj(obj){//通过原型链判断是否obj是一个数组if(objinstanceofArray){varclone=[];}else{varclone={};}for(letiinobj){//如果是对象,递归再往上一层复制if(typeofobj[i]=="object"&&obj[i]!=null){克隆[i]=cloneObj(obj[i]);}else{克隆[i]=obj[i];}}返回克隆;}尝试一下:varobj={name:"NightoftheNorthernLights",like:"aurora",age:{a:[1,2,3],b:2,},};//2.封装一个函数,传入一个对象,返回一个复制的新对象functioncloneObj(obj){//先判断obj是否为数组}else{varclone={};}for(letiinobj){//如果是对象,则递归再进一步复制一层if(typeofobj[i]=="object"&&obj[i]!=null){clone[i]=cloneObj(obj[i]);}else{clone[i]=obj[i];}}返回克隆;}//4.执行函数并得到一个新对象varclone=cloneObj(obj);//5.改变obj属性值obj.age.a[1]="666";//6.输出console.log(clone);结果没问题:当然也可以用Array.isArray(obj)方法判断一个对象是否为数组。如果对象是数组,则返回true,否则返回false。functioncloneObj(obj){//判断obj是否为数组if(Array.isArray(obj)){varclone=[];}else{varclone={};}for(letiinobj){//ifForobjects,递归地更进一步复制if(typeofobj[i]=="object"&&obj[i]!=null){clone[i]=cloneObj(obj[i]);}else{clone[i]=obj[i];}}返回克隆;}4.总结:以上就是深浅拷贝的大概内容。因为对象是引用类型,如果直接把对象赋值给new变量,那么new变量指向的内存和原来的对象是一样的。所以我们通过浅拷贝和深拷贝来开辟自己的内存空间。浅拷贝只复制一层,深拷贝则全部复制。如果文章有什么不对的地方,请指出。点击关注,第一时间了解华为云的新鲜技术~
