前端的深拷贝和浅拷贝_[合格中级前端开发必知]聊聊深拷贝浅拷贝

前言

js的深拷贝,浅拷贝问题在项目的开发过程中是频繁出现的。对于一个合格的前端开发者来说,对于深刻理解它们是必要的,并能合理使用深拷贝浅拷贝处理问题。

什么是深拷贝,什么是浅拷贝

以下我对于它们的理解是:

深拷贝:如果是对象a和对象b它们是深拷贝,那么它们不再引用统一内存地址了,也就是它们 老死不相往来了。

浅拷贝:对象a只是复制指向对象b的指针,而不复制对象本身。新旧对象它们还是共享同一内存。

好了,在脑海里大致知道有这么一个小的概念之后,我们来聊聊更深入的内存。

javascript的堆栈

堆&栈

两者都是存放临时数据的地方。

栈是先进后出的,就像一个桶,后进去的先出来,它下面本来有的东西要等其他出来之后才能出来。

堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。对于堆,我们可以随心所欲的进行增加变量和删除变量,不用遵循次序。

栈区(stack) 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。 堆区(heap) 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。 堆(数据结构):堆可以被看成是一棵树,如:堆排序; 栈(数据结构):一种先进后出的数据结构。

数据类型访问&&复制

基本数据类型:定义在栈中的,从栈当中直接存取变量的值。

例如:var a =1;

栈内存

a

1

基本数据类型复制:复制时,会在栈中创建一个新值,然后把值复制到为新变量分配的位置上。

例如:var b=a;

栈内存

a

1

b

1

b=2

栈内存

a

1

b

2

引用数据类型:定义在栈中的,但它的值指向堆当中的一个地址。

例如:

let obj=Object.create(null);

obj={a:1};

复制代码

栈内存

堆内存

obj

{a:1}

引用数据类型复制:复制的是存储在栈中的指针,将指针复制到栈中并且为新变量分配空间。而这个指针副本和原指针指向存储在堆中的同一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,在使用时,改变其中的一个变量的值,将影响另一个变量。

例如:

let obj=Object.create(null);

obj={a:1};

let obj2=obj;

复制代码

好了,了解了数据结构之后,那我们来谈谈如何实现深浅拷贝。

如何实现深浅拷贝

毋庸置疑,浅拷贝的实现是大多数人都能想到的,因为在项目的开发的过程当中,就是有时候因为数据浅拷贝而引发一系列的数据问题。

源数据data:

let data={

name:'ddd',

type:'es',

impl:[

{

e:1,

a:2

},

{

e:2,

a:3

}

]

}

复制代码

如何实现浅拷贝

第一种情况:将数据源data直接赋值给另外一个对象。 猜猜下面代码结果

let obj=data;//浅拷贝

obj.name='aa'//当obj修改name的值之后,data的name值也发生改变

console.log(obj1,data);

复制代码

第二种情况:将数据源data做为另一个对象的子对象。猜猜下面代码结果

let obj1={data};//定义obj1是一个对象,并且将data是obj1的子对象。

obj1.name='bb'//给obj1添加name属性,data的name值不发生改变

obj1.data.impl[0].e=4; //但是修改obj的data属性的值,数据源data会发生改变

obj1.data.name='pp';

console.log(obj1,data);

复制代码

第三种情况:使用es6的扩展符号{...XXX}。猜猜下面代码结果

let obj2={...data};

obj2.name='cc';

obj2.impl[0].e=3

console.log(obj2,data);

复制代码

第三种的情况是特殊的,但是这种在JavaScript当中是常见的。有时候是深拷贝,有时候是浅拷贝,只有当第一层属性值不是对象的时候则是深拷贝否则就是浅拷贝。 总结一下有时候深拷贝有时候浅拷贝的特殊语法:

1、for in 遍历循环拷贝

2、es6 Object.assign({ },obj)

3、{… obj}

4、concat

5、slice

好吧,上面三种情况的结果依次如下:

第一种情况:

第二种情况:

第三种情况:

那如何才能实现真正的拷贝呢?也就是真的老死不相往来了,一点关系也没有了呢?

如何实现深拷贝

1、利用JSON对象的parse和stringfy

JSON.stringfy是将一个js对象值转成一个JSON字符串,JSON.parse是将一个JSON字符串转成一个js对象。先转成字符串再转对象,所生成的对象和原来的对象不共用同一内存,实现深拷贝的过程。

但是有个问题:undefined、function、symbol在转化中会被忽略。

2、利用递归函数来实现

实现思路就是循环对象,判断对象的属性值是否还是对象,如果还是那就递归。

function cloneData(obj){

let target;

if(typeof obj==='object' && obj!==null){

if(Array.isArray(obj)){

target=[];

}else{

target={};

}

}else{

return target=obj;

}

for(let key in obj){

if(obj.hasOwnProperty(key)){

if(typeof obj[key] ==='object'){

target[key]=cloneData(obj[key]);

}else{

target[key]=obj[key];

}

}

}

return target;

}

复制代码

3、loadsh第三方库来实现 _.cloneDeep

引入第三方库,使用现成api实现深拷贝。

var obj={

a:{

b:2,

c:3

}

}

var obj1=_.cloneDeep(obj);

obj1.a.c=6;

复制代码

总结

以上就是我对深拷贝浅拷贝的理解,上面的代码是无聊的时候瞎写的,小伙伴可以自己动手写一下,有意外的收获的~如果上述的内容有帮到你,那就给我点个赞哈~感谢啦!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值