博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
js中的深拷贝
阅读量:3916 次
发布时间:2019-05-23

本文共 2834 字,大约阅读时间需要 9 分钟。

js深拷贝

什么是浅拷贝?

新的对象复制已有对象中非对象属性的值和对象属性的引用就是浅拷贝。

不理解? 下面用 Object.assign 体现浅拷贝

var obj = {x: 1,y: 2,a: {z: 3}}var obj1 = Object.assign({}, obj)obj.a.z = 4obj.x = 2console.log(obj) // {x: 2, y: 2,  a: {z: 4}console.log(obj1) // {x: 1, y: 2, a: {z: 4}}

从输出结果可以看出,浅拷贝时对非对象是会有新的内存地址的,而对对象的拷贝只是对已存在对象的属性引用,所以当原对象改变时,浅拷贝对象也会改变

什么是深拷贝?

深拷贝会另外拷贝一份一个一模一样的对象,从堆内存中开辟一个新的区域存放新对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

同样的,我们使用 JSON.parse(JSON.stringify()) 来说明深拷贝

var obj = {x: 1,y: 2,a: {z: 3}}var obj1 = JSON.parse(JSON.stringify(obj))obj.x = 2obj.a.z = 4console.log(obj) // {x: 2, y: 2, a: {z: 4}}console.log(obj1) // {x: 1, y: 2, a: {z: 3}}

由上面代码可以看出,深拷贝时,不管是对象还是非对象,都是完全的复制,而不是引用,是拷贝出了一个全新的对象,和原来对象并无关系

深拷贝的实现

最简单的方式

最简单的深拷贝实现方式,即为我们前面使用的方式 ---- JSON.parse(JSON.stringify())

function clone(target){  return JSON.parse(JSON.stringify(target))}

这个方法虽然是最简单的,但却是最实用的,因为大部分的场景都能用这条简单的语句解决。但显然,既然不能解决所有问题,那它就是不完善的。

基础方式

function clone1(target) {  if(typeof target === 'object') {    let cloneTarget = {}    for(const key in target){      cloneTarget[key] = clone1(target[key])    }    return cloneTarget  }else {    return target  }}

这里我们使用递归来处理深拷贝,如果是原始类型,直接返回;如果是引用类型,创建一个新对象,将对象内的属性深拷贝至新对象中,一直到最终的对象中为原始类型为止

基础方式修复

之前的基础方式有一个问题----我们并没有处理数组对象,如果对象是数组呢?

function newClone1(target) {  if(typeof target === 'object') {    let cloneTarget = Array.isArray(target) ? [] : {}    for(const key in target){      cloneTarget[key] = newClone1(target[key])    }    return cloneTarget  }else {    return target  }}

其实处理方式非常简单,只要在创建新对象时判断对象是否为数组。是,则创建数组对象

解决引用自身问题

如果使用之前的基础方式,其实是有问题的,如下:

var target = {a: 1, b: {c: 2}, c: [1, 2, 3, 4, 5]}target .d = target let cloneTarget = newClone1(target)// 报错信息如下/*Uncaught RangeError: Maximum call stack size exceeded    at newClone1 (
:1:19) at newClone1 (
:5:26) at newClone1 (
:5:26) at newClone1 (
:5:26) at newClone1 (
:5:26) at newClone1 (
:5:26) at newClone1 (
:5:26) at newClone1 (
:5:26) at newClone1 (
:5:26) at newClone1 (
:5:26)*/

这是因为循环引用,递归永远出不来导致的栈溢出,那么,我们如何解决这个问题呢?

其实也比较简单,我们需要一个新的数据类型—Map
在 JavaScript 的对象,本质上是键值对的集合,但传统上只能使用字符串当做建,这就对它的使用产生了限制。
在ES6中,提供了 Map 数据结构。它也是键值对的集合,但是‘键’的范围不仅是字符串,Map提供的是‘值-值’。
并且,Map 有对应的 WeakMap,可以自动进行回收内存
考虑这种特性,我们可以对之前的拷贝进行完善
如果想详细了解 Map ,可以前往 http://es6.ruanyifeng.com/#docs/set-map

function clone2(target, map = new WeakMap()) {  if(typeof target === 'object') {    let cloneTarget = Array.isArray(target) ? [] : {}    if(map.get(target)){      return map.get(target)    }    map.set(target, cloneTarget)    for(const key in target){      cloneTarget[key] = clone2(target[key], map)    }    return cloneTarget  }else {    return target  }}

到这里,我们基本上算是解决了绝大部分的对象深拷贝。既然是绝大部分,那么说明还是存在一些问题

如果你还不满足于这样的深拷贝
推荐你去看这篇文章: https://juejin.im/post/5d6aa4f96fb9a06b112ad5b1
ps: 我的这篇文章基本是学习这位大牛

转载地址:http://hxprn.baihongyu.com/

你可能感兴趣的文章
平台or职位,你怎么选?
查看>>
骚年快答 | 技术中台与业务中台都是啥?
查看>>
骚年快答 | 微服务架构中的BFF到底是啥?
查看>>
设计模式之适配器模式
查看>>
如何利用Gitlab-CI持续部署到远程机器?
查看>>
.NET Core + K8S + Loki 玩转日志聚合
查看>>
ASP.NET Core中的分布式缓存
查看>>
在ASP.NET Core中创建自定义端点可视化图
查看>>
继续分享 5 个实用的 vs 调试技巧
查看>>
五年了,别再把务虚会开 “虚” 了
查看>>
一文看懂"async"和“await”关键词是如何简化了C#中多线程的开发过程
查看>>
每天都在支付,你真的了解信息流和资金流?
查看>>
.Net Core 自定义配置源从配置中心读取配置
查看>>
基于.NetCore3.1系列 —— 日志记录之日志配置揭秘
查看>>
设计模式之享元模式
查看>>
单例模式最佳实践
查看>>
.NET Core + Spring Cloud:服务注册与发现
查看>>
今天你内卷了吗?
查看>>
设计模式之代理模式
查看>>
在 MySQL 中使用码农很忙 IP 地址数据库
查看>>