深拷贝与浅拷贝(方法剖析)

 先来看几个小案例:

 

 

 

P1:循环递归法

注:

深拷贝是针对引用类型的, 在进行深拷贝之前, 我们应该先知道js中有哪些引用类型,

js中引用类型目前有六种: object, array, date, regexp,

function, err。 下面的两种方法只能实现object, array的深拷贝。

循环递归法 function、Date、RegExp 和Error无法复制 因为它们有特殊的构造函数。

 

 P2: 循环递归法2

注:

此方法与P1大同小异---对传入的obj进行了解构处理并使用静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组。Reflect.ownKeys 方法返回一个由目标对象自身的属性键组成的数组

 

P3:序列化反序列化法实现深拷贝

使用JSON对象的parse和stringify方法来实现深拷贝

注:

它也只能深拷贝对象和数组,对于其他种类的对象,会失真。

这种方法比较适合平常开发中使用,因为通常不需要考虑对象和数组之外的类型。

拷贝的对象的值中如果有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失

无法拷贝不可枚举的属性, 无法拷贝对象的原型链

拷贝Date引用类型会变成字符串

拷贝RegExp引用类型会变成空对象

对象中含有NaN、 Infinity和 - Infinity, 则序列化的结果会变成null

无法拷贝对象的循环应用(即obj[key] = obj)

 

P4:lodash中深拷贝的实现

注:

Lodash是一个轻量级的JavaScript工具函数库, 它方便了日常开发中对数据的操作, 提高了开发效率。

著名的 lodash 中的 cloneDeep 方法同样是使用 Reflect 法 实现的,

只不过它支持的对象种类更多, 具体的实现过程读者可以参考 lodash 的 baseClone 方法。

lodash可以完成array、 object、 date、 regexp的深拷贝, 但

function 和 error 仍然不可拷贝

https://github.com/lodash/lodash/blob/master/.internal/baseClone.js

​ 日常开发中,通常会对数据,特别是数组和对象进行各种读写等操作:

 比如去重,拷贝,合并,过滤,求交集,求和等等。根据平时开发中对数据的操作,

 

P5:对象成环怎么办?

我们给 test 加一个 loopObj 键,值指向自身:

test.loopObj = test

这时我们使用第一种方法中的 for..in 实现和 Reflect 实现都会栈溢出:

环对象深拷贝报错

而使用第二种方法也会报错:
 

但 lodash 却可以得到正确结果:

因为 lodash 使用的是栈把对象存储起来了,如果有环对象,就会从栈里检测到,

从而直接返回结果,悬崖勒马。这种算法思想来源于 HTML5 规范定义的结构化克隆算法,

它同时也解释了为什么 lodash 不对 Error 和 Function 类型进行拷贝。

当然,设置一个哈希表存储已拷贝过的对象同样可以达到同样的目的:

 这里我们使用 WeakMap 作为哈希表,因为它的键是弱引用的,而我们这个场景里键恰好是对象,需要弱引用。

P6:易混淆点

1、

如果键值不是字符串而是 Symbol,我们会发现一个问题!!!!

会拷贝失败了, 为什么?

原因其实很简单:因为 Symbol 是一种特殊的数据类型, 它最大的特点便是独一无二, 所以它的深拷贝就是浅拷贝

 

2、

但如果这时我们使用 Reflect 实现的版本:

成功了,因为 for...in 无法获得 Symbol 类型的键,而 Reflect 是可以获取的。

当然,我们改造一下 for...in 实现也可以:

 

for...in 会追踪原型链上的属性,

而其它三种方法(Object.keys、Reflect.ownKeys 和 JSON 方法)都不会追踪原型链上的属性:

P7:如需要拷贝不可枚举的属性

第四种情况, 就是我们需要拷贝类似属性描述符, setters 以及 getters 这样不可枚举的属性, 一般来说, 这就需要一个额外的不可枚举的属性集合来存储它们。 类似在第二种情况使用

for... in 拷贝 Symbol 类型键时:

我们给 test 变量里的 obj 和 arr 属性定义一下属性描述符:

 

P8:日常深拷贝,建议序列化反序列化方法。

一、面试时遇见面试官搞事情,写一个能拷贝自身可枚举、自身不可枚举、自身 Symbol 类型键、原型上可枚举、原型上不可枚举、原型上的 Symol 类型键,循环引用也可以拷的深拷贝函数;

二、有特殊需求的深拷贝,建议使用 lodash 的 copyDeep 或 copyDeepWith 方法。

如下:

将之前写的 deepClone 函数封装一下

 注:completeAssign这个函数会拷贝所有自有属性的属性描述符,来自于 MDN

下面这个函数会拷贝所有自有属性的属性描述符,来自于 MDNicon-default.png?t=MBR7https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

欢迎指正!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值