当前位置: 首页 > Web前端 > HTML

JavaScript的浅拷贝和深拷贝,栈和堆,深拷贝的多种方法

时间:2023-03-28 13:28:38 HTML

栈和堆在JavaScript中,基本类型和对象类型也被称为值类型和引用类型。这是因为基本类型存储了数据的完整值,而引用类型只存储了指向数据的一个地址。在大多数编程语言中,变量存储在两个地方,栈和堆。在JavaScript中,栈存放的是值类型的数据和引用类型的地址,而真正的引用类型的数据存放在堆中。当我们定义如下变量时,它们在内存栈中的存在如图所示:,age:20,}letstudents=["Naruto","Luffy","Conan"]深拷贝和浅拷贝这样会造成值类型和引用类型的拷贝有区别结果,一个值的拷贝类型是相同值的精确副本,而引用类型的副本是引用地址的副本。letname="Naruto"letperson={name:"Ningji",age:20,}letname2=nameletperson2=person对于引用(对象)类型,这种只是复制对象引用地址的方式称为浅拷贝(shallowcopy),与之相对应的,如果在堆中复制完全相同的数据,则称为深拷贝(deepcopy)。将对象数据存储在堆中可以大大节省内存空间。如果把中国的整个列表存到一个变量里,用栈来存,每次都会把整个数据拷贝一份(每次调用函数传参也是拷贝一份),内存根本就不够用足够。堆中存储的数据每次只复制一个引用地址,大大节省了内存占用。浅拷贝当我们修改浅拷贝对象中的数据时,指向数据地址的多个变量都会受到影响。请注意,有时此功能会导致意外问题。letperson={name:"Ningci",age:20,}letperson2=personperson2.name="Conan"alert(person.name)//Conan的数组也是对象,所以也有浅拷贝的特点。letarr=[1,2,3]letarr2=arrarr2[0]="x"alert(arr[0])//x浅拷贝只是一个特性,它也有好有坏。假设我们有一个人的对象,在代码中的多个地方都用到了,这个人改了一个名字,那么这个人的名字在别处使用的时候就会使用新的名字,而我们不需要修改每一个的名字目的。这非常符合生活场景,具有实际意义。让mingren={name:"Naruto",score:40,}functionmark(person){if(person.score<60){person.status="Fail"}else{person.status="Pass"}}mark(mingren)alert(mingren.status)//不合格的深拷贝深拷贝是在堆中拷贝数据的结果,这样对拷贝对象的操作不会影响到原对象。letperson={name:"Ningci",age:20,}letperson2={...person}再次修改person2,不会对person产生任何影响。letperson={name:"Ningji",age:20,}letperson2={...person}person2.name="Naruto"alert(person.name)深拷贝一般有以下几种方法:使用解构赋值使用Object.create,使用JSON.parse和JSON.stringify,使用structuredClone自己实现,使用第三方库,比如lodashdestructuringassignment。解构赋值有问题。对象中只能复制第一层的值,更深的一层是无能为力的。letperson={name:"Ningji",age:20,sister:{name:"Hinata",age:18,},}letperson2={...person}person2.name="火影忍者"alert(person.name)//宁次,第一层okperson2.sister.name="明美"alert(person.sister.name)//明美,第二层还是浅拷贝//数组的解构赋值是深拷贝letarr=[1,2,3]letarr2=[...arr]arr2[0]="x"alert(arr[0])//1Object.createObject.create使用现有对象创建新对象。同理,也只能解决第一层的复制。有关详细信息,请参阅Object.create-MDN。letperson={name:"Ningji",age:20,sister:{name:"Hinata",age:18,},}letperson2=Object.create(person)person2.name="火影忍者"alert(person.name)//宁次,第一层okperson2.sister.name="明美"alert(person.sister.name)//明美,第二层依然是浅拷贝注意:Object.create是针对数组的深拷贝不理想。JSON.parse和JSON.stringify先使用JSON.stringify将对象序列化为字符串,再使用JSON.parse解析回来,可以达到深拷贝的效果,对多层对象也有效.letperson={name:"Ningci",age:20,sister:{name:"Hinata",age:18,},}letperson2=JSON.parse(JSON.stringify(person))person2.name="鸣人"alert(person.name)//Neji,第一层okperson2.sister.name="Naruto"alert(person.sister.name)//Hinata,多层对象还是OK注意:JSON.parse和JSON.stringify仍然不完美,因为无法有效复制某些方法或用户定义的类型。letperson={name:"Ningci",age:20,sayHello(){alert("Hello~")},}letperson2=JSON.parse(JSON.stringify(person))person2.sayHello()//错误:person2.sayHelloisnotafunctionstructuredClonestructuredClone是一个内置的方法,可以很好地复制多层对象,但对于方法或自定义对象同样无能为力。有关详细信息,请参阅structuredClone-MDN。letperson={name:"Ningji",age:20,sister:{name:"Hinata",age:18,},}letperson2=structuredClone(person)person2.name="Ningji"alert(person.name)//宁次,第一层okperson2.sister.name="明美"alert(person.sister.name)//Hinata,多层对象还是OK的。与方法或自定义对象无关。letperson={name:"Ningci",age:20,sayHello(){alert("Hello~")},}//未捕获的DOMException:Failedtoexecute'structuredCloneon'Window':sayHello(){alert("Hello~")}couldnotbeclonedletperson2=structuredClone(person)自行实现的灵活性很高,根据代码实现的不同,效果和运行效率也会有所不同。下面是三眼鸭通过递归实现的深拷贝功能。经测试,可以正确复制常见的多层对象、数组和方法。//深拷贝functiondeepclone(obj){functioncopyList(arr){letresult=[]for(letitemofarr){result.push(this.deepclone(item))}returnresult}if(typeofobj==="object"){if(Array.isArray(obj)){returncopyList(obj)}else{letresult={}for(letkeyinobj){result[key]=deepclone(obj[key])}returnresult}}else{returnobj}}letperson={name:"Ningji",age:20,sayHello(){alert("Hello")},sister:{name:"Hinata",age:18,},arr:[1,2,3],}letperson2=deepclone(person)person2.name="Naruto"alert(person.name)//宁次,第一层okperson2.sister.name="明美"alert(person.sister.name)//Hinata,多层对象还是okperson2.arr[0]="x"alert(person.arr[0])//1,数组也是okperson2.sayHello()//你好,方法没问题使用lodashLodash是一个第三方库,扩展了多种JavaScript的功能。官方介绍:Lodash使JavaScript更容易处理数组、数字、对象、字符串等。Lodash的模块化方法非常适合:迭代数组、对象和字符串操作和测试值创建复合函数更多详细信息,请参阅lodash官方文档。lodash的引入方式有很多种,下面介绍使用外链的方式。letperson={name:"Ningci",age:20、sayHello(){alert("Hello")},姐姐:{name:"Hinata",age:18,},arr:[1,2,3],}letperson2=_.cloneDeep(person)person2.name="Naruto"alert(person.name)//Neji,第一层okperson2.sister.name="Naruto"alert(person.sister.name)//Hinata,多层物体StillOKperson2.arr[0]="x"alert(person.arr[0])//1,数组也可以person2.sayHello()//你好,方法也可以三眼鸭总结了以下三个关键点:当使用shallow复制,注意数据的修改。如果你想知道修改是否会影响所有引用的对象,结果就是你想要的。如果没有,请使用深拷贝。对于函数内部的代码,要尽量避免修改传入的对象。如果必须修改,必须在功能描述中突出显示。也不要盲目频繁地使用深拷贝,以免造成大量的内存浪费。对于深拷贝,不一定要选择大而全的全拷贝方式。如果只是单层对象或数组,单纯使用解构赋值是最好的方法。今日名言“任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员可以编写人类可以理解的代码。”–马丁福勒–MartinFowler感谢您的点赞,三眼鸭祝您编程学习成功。