ES6 深拷贝_JS深浅拷贝探究

cf3cd517839bd7c9e932ccbeb296531d.png

第0章 前言

之前面试时面试官问到 JS 的浅拷贝和深拷贝,虽说之前有了解过,但因为没有总结的缘故,答得并不好,故想着把内容整理一番,便照做了。

0-1 场景导入

我们先来看一个例子如下一个例子:

let 

是不是答对了呢?

没错,以上例子就是我们俗称的 浅拷贝 了。那么,为什么会出现上述这种 直接将对象赋值给一个新变量,修改新对象的值会改变原对象的值 的现象呢?这就要从JS的数据类型存储方式说起了。


0-2 回顾 · 数据类型

众所周知,JavaScript存在两种数据类型分别称为 基本数据类型引用数据类型

  • 基本数据类型:Boolean、Null、Undefined、Number、String、Symbol(ES6新增)
    • 键和值均存储于栈内存中。
  • 引用数据类型:Object、Array、Function、RegExp、Date等。
    • 键和值的地址存储于在栈内存,值存储于堆内存。

第1章 浅拷贝

那么其实就说得通了:其实在表面上变量里是直接存储了一个对象,但实际上是存储了一个对象的地址,而这个地址指向该对象。

所以,直接将对象赋值给一个新变量,实际上是将原变量的地址赋值给了新变量,故你操作新变量的过程,实际上还是操作原对象

例如以下示例:

// 定义一个数组

第2章 深拷贝(重难点)

2-1 完整实现

那么,如果我们就想要拷贝一个对象过去并形成独立的空间呢?

这就要用到我们的 深拷贝 技术啦。

我们已知直接将对象赋值给一个新变量只是做了浅拷贝(只拷贝对象地址)。

如何实现?看代码:

let 

所以,由上例子可知,深拷贝的原理就是定义一个新的对象,遍历源对象的属性并赋给新对象的属性

2-2 通用的简易方案

虽然我们刚刚已经实现了对象的深拷贝,但是这样封装一个函数的做法内容还是显得过于冗长了。有的时候我们也只是想深拷贝一个对象,不存在复用性。这个时候采用完整的封装函数的做法就未免显得过于愚钝。

那么,我们是否还有更加简洁的做法呢?

答案是肯定的!(这真令人振奋)

看代码:

let 

如上,是不是一行代码就轻松解决了对象的深拷贝呢?(如果真有这么美好就好了...)

实际上并不然。

因为,这种做法其实会忽略值为 undefined、function 和 symbol 的内容。

所以啊,想要完整实现深拷贝的功能还是老老实实用第一种函数封装的方式吧~

2-3 只对数组适用的简易方案

虽说数组也是对象,但数组相对标准的对象就比较特殊。它们可以两种简易的方案完成数组自身的深拷贝。

2-3-1 通过 slice 实现

slice 是 Array 的 API,其本身的作用是从已有的数组中返回选定的元素。
// 定义一个数组

2-3-2 通过 concat 实现

concat 是 Array 的 API,其本身的作用是完成数组间的的连接。
// 定义一个数组

第3章 总结

  • 浅拷贝
    • 直接将对象赋值给新变量即可实现浅拷贝。
    • 实际上是拷贝了一个对象的地址。
    • 新对象的值发生改变,旧对象的值也因变而变。
  • 深拷贝
    • 定义一个新的对象,遍历源对象的属性并赋给新对象的属性即可实现深拷贝。
    • 深拷贝是完全拷贝了原对象的内容并寄存在新的内存空间,指向新的内存地址。
    • 新对象的值发生改变,旧对象不影响(因为实际上这已经是两个对象,毫无关联性了)
  • 如图

45eefbeaee3b0270d908e5fe4064c222.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值