前言
关于 JS 的深拷贝,相信不少同学都有所耳闻或者了解吧,那为什么会有深拷贝?深拷贝解决了什么问题?深拷贝的实现逻辑是什么?作为前端面试的高频考点,今天这篇文章就带大家吃透深拷贝,搞懂它的前因后果
前因—浅拷贝
在讲浅拷贝之前,先来了解一下什么是值类型和引用类型,会的同学就当复习一波~
值类型和引用类型
什么是值类型和引用类型?一个例子教大家看懂:
值类型:
这个大家应该都能理解,比较直观的逻辑,打印的b结果为100。看下面逻辑图加深印象
值类型都是通过栈进行存储的:
- 第一步:把a赋值成100
- 第二步:把b赋值为a,也是等于100
- 第三步,把a赋值为100,a和b互不影响,b的值仍未100
引用类型:
a赋值成一个对象age:20,b赋值为a,b.age赋值为21,最后打印a.age的值也为21。看下面逻辑图加深印象:
这里除了上面的栈,还引入了一个新的概念堆。在计算机的存储中,栈从上往下排列,堆从下往上排列,一般不会重合。来看一下上面例子的存储过程:
- 第一步:给age赋值成一个等于20的对象,他的key会给一个内存地址一,此时变量a指向的是一个内存地址1,也就是说a里面存储的并不是这个对象,而是存入了内存地址指向这个对象
- 第二步:a指向内存地址一,b也指向内存地址一,看似a等于age:20,b也等于age:20
- 第三步:b.age等于21的话,肯定会把下面的对象改为age:21,因为a指向的也是这个地址,所以a.age也是21
区别: 值类型各自赋值,不会相互干扰;引用类型上面b改变了a也会随之改变
看完了上面,下面给大家上个题,练习练习:
正确答案是shanghai,跟我们上面第二个例子的逻辑过程一样,这样的过程我们就称之为“浅拷贝”。
那么有没有什么办法能像赋值值类型一样,将引用类型也去以这种方式赋值呢?也就是改变obj2的值,obj1的值不改变。
正是为了解决这个问题,所以有了我们今天的主角——深拷贝
后果—深拷贝
关于深拷贝
- 会克隆出一个对象,数据相同,但是引用地址不同
上面这句话大家是不是觉得有点抽象,下面我们利用上题obj的例子来带大家实现一下深拷贝:
扫描二维码关注公众号,回复:
13554546 查看本文章
手写深拷贝
先来直接上代码,再给大家分析一波(代码关键在于理解递归):
function deepClone(obj = {
}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是对象和数组,直接返回
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {
}
}
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
我们直接定义这个深拷贝的函数:
- 第一步:先进行判断,因为深拷贝针对对象来进行,所以如果obj为空或者不是对象和数组,我们直接返回这个obj
- 第二步:先初始化结果,然后进行判断,obj instanceof Array是判断它是否为数组,数组则返回数组格式,对象也就返回对象的格式
- 第三步:进行一个for遍历,遍历对象中的每一个key,obj.hasOwnProperty(key)是保证key为每个对象自己所拥有的属性
- 第四步:result[key] = deepClone(obj[key]),进行递归调用,因为对象中有的key会嵌套很对层,使用递归保证到判断到每一层的属性
- 最后就是返回结果
写完之后直接调用这个函数
const obj2 = deepClone(obj1)
console.log(obj1.address.city)
再来看结果,改变obj2的值后,obj1的值并没有发生改变,这样我们的目的就达到了
我们再来看看深拷贝的实现逻辑图:
- 一样的有栈和堆,key值a存储内存地址1,指向对象{ age:20 }
- b进行深拷贝a,此时会克隆出一个内存地址2,指向的内容与地址1相同,并且b当中存入的也是新生产的内存地址2。所以数据相同,但是引用地址不同。
- 因为引用的地址不同,所以b改变,a不会变。
今天分享的内容就到这里了,对你有帮助的话点赞支持~有问题可以在下方交流