深度拷贝的实现思路是遍历对象的 property 取值然后进行递归调用。(代码部分假设该对象的属性均为 object)

function fn(object) {for (var key in object) {fn(object[key]); // 递归调用}
}
复制代码

在每一次函数执行开始前创建一个新的 object,在遍历 property 的同时赋予新对象相同的属性。

function fn(object) {var cloneObj = {};for (var key in object) {// 赋予新对象相应的 property// 通过递归调用来拷贝 property 的值cloneObj[key] = fn(object[key]);}// 返回新对象return cloneObj;
}
复制代码

假如有这样一个对象,就会让这个函数陷入死循环。

var obj = {};
obj.a = obj; // obj 的 a 属性 引用 obj,形成循环引用// obj 的结构是这个样子
obj: {a: {a: {a:{...}}}
}复制代码

看了 Lodash 的 cloneDeep 函数提供了解决思路,创建一个在整个函数执行期间一直存在的 Map 类型变量(Map 可以设置除 string 以外的类型作为 key,也是键值对的结构,这一点比 object 要灵活),每一次函数执行把这一轮传入的对象作为 key 新的创建对象作为值,在函数开头执行判断传入的对象是否已经存在 map 的 keys 中,如果存在那代表着传入的对象在之前已经遍历过了

完整代码

function fn(object) {// 首先判断 object 是否存在于 map.keys 中if (Array.from(map.keys()).includes(object)) {// 如果存在则取出值并返回return map.get(object);}var cloneObj = {};// 设置 object 为 key,cloneObj 为值map.set(object, cloneObj);for (var key in object) {// 赋予新对象相应的 property// 通过递归调用来拷贝 property 的值cloneObj[key] = fn(object[key]);}// 返回新对象return cloneObj;
}var obj = {};
obj.a = obj;var map = new Map();fn(obj);
复制代码

阐述一下具体执行过程

第一次循环,把 obj 作为 key,cloneObj 作为值存入map

map.set(object, cloneObj);
// result: map { obj: cloneObj }
复制代码

遍历 obj 的属性(这里只有一个属性 a)

for (var key in obj) {}
复制代码

给 cloneObj 添加上相同的属性名为 a,但是 obj.a 值我们并不知道,所以使用递归依次向下取值

for (var key in obj) {cloneObj[a] = fn(obj[a]);
}
复制代码

注意在这之前初始化 obj 的时候,obj 的属性 a 是等于 obj的

var obj = {};
obj.a = obj
复制代码

所以

fn(obj[a]) 相当于 fn(obj)
复制代码

现在进入第二次循环

// 此时 obj 已经存在于 map.keys 中
// map { obj: cloneObj }
if (Array.from(map.keys()).includes(obj)) {return map.get(obj); // 返回第一次循环中的 cloneObj
}复制代码

从这里第二次循环就结束了,返回了第一次循环中的 cloneObj

// 函数执行结束,回到第一次循环
for (var key in obj) {cloneObj[a] = cloneObj; // fn(obj[a])的返回值
}
// 赋值语句执行过后 cloneObj 的结构是这样的
cloneObj: {a: {a: {...}}
}
复制代码

这里 cloneObj 的结构就跟 obj 完全一样了。

转载于:https://juejin.im/post/5c7bc9c16fb9a049ec6bcb24

记录如何深度拷贝一个属性存在循环引用的对象相关推荐

  1. python 带随机指针的链表深度复制_链表--深度拷贝一个带有随机指针的链表

    链表--深度拷贝一个带有随机指针的链表 链表--深度拷贝一个带有随机指针的链表 本文介绍两种解法. 解法1:利用一个map ListNode *copyRandomList(ListNode *hea ...

  2. python 引用计数 循环引用_Python对象的循环引用问题

    Python对象循环引用 我们来介绍一下 Python 是采用何种途径解决循环引用问题的. 循环引用垃圾回收算法 上图中,表示的是对象之间的引用关系,从自对象指向他对象的引用用黑色箭头表示.每个对象里 ...

  3. GSON 循环引用的对象转为 JSON 造成栈溢出

    对象转 JSON 可能引发栈溢出的异常,一般是因为对象中的循环引用引起不断递归. 常见的作法就是: 换一种 JSON 的序列化工具,比如 fastjson 默认支持消除对同一对象循环引用 transi ...

  4. html 删除一个属性值,html之DOM对象removeAttribute()方法(删除节点属性)功能简介说明...

    摘要: 下文讲述DOM对象removeAttribute()方法功能说明,如下所示: DOM removeAttribute()方法功能说明 DOM removeAttribute()方法功能(): ...

  5. Javascript 深度克隆中的循环引用问题解决和代码优化

    概要 在前端项目开发中,我们经常需要深度克隆JS对象.在克隆代码开发过程中,我们经常会遇到数组判定或对象循环引用的问题. 本文通过实例来解决上述问题 代码及实现 常见深度克隆JS对象的代码 funct ...

  6. 将对象拷贝一份备用,改变原对象的属性时,为啥备份数据也改变了?(赋值、浅拷贝和深拷贝的区别)

    前言 前两天在写代码时碰到一个很奇怪的问题,我有一个对象person = {name: ''tianxin, age: 18};,我需要将person目前的数据拷贝一份备用let copyPerson ...

  7. Block 本质、实现原理、内存管理、循环引用、__block等

    一.Block介绍 1.1概念: 将函数及其执行上下文封装起来的对象 底层用struct实现 1.2block实现原理: a .新建项目 代码放入file.m中 b.打开终端cd到项目目录下 c.敲c ...

  8. 浅析 SpringMVC 中返回对象的循环引用问题

    问题发现 今天这个话题还是比较轻松的,可能很多朋友也都遇到过这个问题. @RestController.@ResponseBody 等注解是我们在写 Web 应用时打交道最多的注解了,我们经常有这样的 ...

  9. spring生命周期七个过程_Spring杂文(三)Spring循环引用

    众所周知spring在默认单例的情况下是支持循环引用的 Appconfig.java类的代码 @Configurable @ComponentScan("com.sadow") p ...

最新文章

  1. android 绘画,Android绘图基础
  2. vue实现动态css,Vue中如何动态绑定CSS
  3. 牛客网 2018年全国多校算法寒假训练营练习比赛(第三场)D.小牛vs小客-博弈
  4. Android音频播放实例
  5. 用JavaScript实现简单的excel列转sql字符串
  6. STM32系统学习——DMA(直接储存器访问)
  7. Node.js的环境搭建
  8. HDLBits答案(17)_Verilog有限状态机(4)
  9. mq服务器与客户端消息同步,使用 ActiveMQ 实现JMS 异步调用
  10. 排错“未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配”...
  11. PHP 败给 Python 的十大理由
  12. Total capture: A 3D Deformation Model for tracking faces, hands and bodies
  13. JDBC和数据库连接池的关系
  14. 十六进制的字符对照表
  15. Unity制作游戏自定义按键
  16. 关于word中最后一个分节符导致的空白页删除问题
  17. 硬件工程师成长之路(10)——项目举例
  18. 为什么说中小学编程教育是创新思维体操
  19. 公司企业邮箱怎么开通注册?仅需2步,教大家公司企业邮箱怎么弄
  20. 计算机考研失败了还能找工作吗,考研失败了怎么办_考研失败还能找工作吗

热门文章

  1. 数据结构——平衡二叉树
  2. mkdir touch vim
  3. 练习1:创建一个简单的银行程序包
  4. 蓝牙 MultipeerConnectivity
  5. python爬虫2——下载文件(中华网图片库下载)
  6. hdu1161 欧拉路
  7. UIAlertControl的使用对比与UIAlertView和UIActionSheet
  8. android ListView和GridView拖拽移位具体实现及拓展
  9. 专科计算机专业大学,计算机专业大学排名 专科生学计算机专业好吗
  10. oracle 安装ora 27102,Oracle ora-27102 错误