首页>>前端>>JavaScript->从浅拷贝与深拷贝发现JSON.stringify的 “魅力”

从浅拷贝与深拷贝发现JSON.stringify的 “魅力”

时间:2023-11-29 本站 点击:0

基本类型

基本数据类型有7种:NumberStringBooleanNullUndefinedSymbol(ES6)BigInt(ES10)。变量均按值存放于栈中,赋值直接用=即可。

引用类型

引用数据类型有1种:Object。变量内存地址存放于栈中,值存在堆中,引用类型的赋值与下面讨论的浅拷贝深拷贝密切相关。

浅拷贝

首先声明 浅拷贝 ≠ 赋值

赋值=赋的是对象的内存地址,两个对象指向堆中同一份存储空间,互相影响。

letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=obj1obj2.name='七金'obj2.list[0]='Java'//{name:'七金',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}

浅拷贝是在堆中创建新的内存空间,拷贝后对象的基本数据类型互不影响,但引用类型依然共享同一份存储空间,会互相影响。

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}

Array

整理可数组浅拷贝相关api

扩展运算符

letarr1=[1,[2],3]letarr2=[...arr1]arr2[0]=4arr2[1].push(5)console.log(arr1,arr2)//[1,[2,5],3][4,[2,5],3]

Array.prototype.slice

letarr1=[1,[2],3]letarr2=arr1.slice()arr2[0]=4arr2[1].push(5)console.log(arr1,arr2)//[1,[2,5],3][4,[2,5],3]

Array.prototype.concat

letarr1=[1,[2],3]letarr2=arr1.concat([])arr2[0]=4arr2[1].push(5)console.log(arr1,arr2)//[1,[2,5],3][4,[2,5],3]

Array.from

将类数组或可迭代对象创建一个新的浅拷贝数组实例。

letarr1=[1,[2],3]letarr2=Array.from(arr1)arr2[0]=4arr2[1].push(5)console.log(arr1,arr2)//[1,[2,5],3][4,[2,5],3]

Array.prototype.map

letarr1=[1,[2],3]letarr2=arr1.map(item=>item)arr2[0]=4arr2[1].push(5)console.log(arr1,arr2)//[1,[2,5],3][4,[2,5],3]

Array.prototype.filter

letarr1=[1,[2],3]letarr2=arr1.filter(item=>item)arr2[0]=4arr2[1].push(5)console.log(arr1,arr2)//[1,[2,5],3][4,[2,5],3]

Array.prototype.reduce

reduce这里可能有点滥竽充数?,没有真正体现它的价值,算是提供一种新奇的思路吧。

letarr=[1,[2],3]letarr2=arr.reduce((arr1,item)=>{arr1.push(item)returnarr1},[])arr2[0]=4arr2[1].push(5)console.log(arr,arr2)//[1,[2,5],3][4,[2,5],3]

Object

整理可对象浅拷贝相关api

扩展运算符

letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2={...obj1}obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}

Object.assign

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}0

深拷贝

堆内存重新开辟全新的内存存放新对象,两个对象不会互相影响。

Array

序列化

利用JSON.stringify将数组转为JSON字符串,再用JSON.parse将字符串转为新数组。

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}1

Object

序列化

利用JSON.stringify将对象转为JSON字符串,再用JSON.parse将字符串转为新对象,但这个方法存在弊端。

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}2

貌似看起来没有任何问题,也不用引入lodash库。那么,现在给对象添加个方法看看~

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}3

方法在JSON.stringify后丢失了... 万事总有解决办法,实在不行引lodash库。

我当时处理方法是将函数转为字符串确保不再丢失,最后再利用new Function()去将字符串转为函数。

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}4

当然这个方法也并不是完美的,比如我确实有个字段为string类型,且值就是function,那就真是凑巧了。

这个现象引发了我对JSON.stringify的兴趣,还会丢失哪些类型的数据?列举了写属性...

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}5

发现真的是深坑?‍♀️

函数、Symbolundefined丢失

NaNInfinitynull

RegExp对象变{}

Date对象转为字符串

继续在网上找JSON.stringify踩坑文章,以下情况也得小心 5. 方法自带toJSON, 直接返回函数return

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}6

属性引用自身,会报错

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}7

存在不可枚举属性,也会丢失

functionshallowClone(obj1){letobj2={}for(letiinobj1){obj2[i]=obj1[i]}returnobj2;}letobj1={name:'瑾行',list:['JS','CSS','HTML']}letobj2=shallowClone(obj1)obj2.name='七金'obj2.list[0]='Java'console.log(obj1,obj2)//{name:'瑾行',list:['Java','CSS','HTML']}//{name:'七金',list:['Java','CSS','HTML']}8

这些情况,有一些同样可以先转字符串,再转回原属性类型,算是一种思路吧,但都在对象value情况可知的大前提下,不然还是用成熟的lodash中的cloneDeep吧。

深拷贝这么复杂,准备之后研究lodash中的cloneDeep源码,手写试试看,先起个草稿。 如果大家觉得有帮助,欢迎点赞、交流学习

作者:瑾行著作权归作者所有。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/JavaScript/660.html