概要

在前端项目开发中,我们经常需要深度克隆JS对象。在克隆代码开发过程中,我们经常会遇到数组判定或对象循环引用的问题。

本文通过实例来解决上述问题

代码及实现

常见深度克隆JS对象的代码

function deepClone(origin){if (origin == undefined || typeof origin !== "object"){return origin;}if (origin instanceof Date){return new Date(origin);}let keys = Reflect.ownKeys(origin);let target = {};if (Object.prototype.toString.call(origin) === "[object Array]"){target = [];}for(let key of keys){target[key] = deepClone(origin[key]);}return target;
}

上述代码主要存在两个问题:

  1. 数组判定代码过于麻烦
  2. 对象中如果出现循环引用,代码将会报错。

循环引用代码:

var obj = {a: 1};
obj.obj1 = obj;
var newObj = deepClone(obj);
console.log(newObj);

错误信息:

产生错误的原因很简单,由于对象中存在循环引用,所以递归无法结束,最后内存溢出了。

循环引用问题的解决

由于对象中的循环引用,所以递归无法结束。如果我们记录下哪个对象被克隆了,哪个对象没有被克隆,就可以强制结束递归,避免内存泄漏。

我们选择WeekMap来记录对象是否被克隆,主要考虑以下三点:

  1. WeakMap对象是key => value形式,不会重复记录
  2. WeakMap对象的key必须是一个对象
  3. WeakMap是若引用,如果不再使用,内存空间直接释放。

改进代码如下:

function deepClone(origin, map = new WeakMap()){if (origin == undefined || typeof origin !== "object"){return origin;}if (origin instanceof Date){return new Date(origin);}if (origin instanceof RegExp){return new RegExp(origin);}var copied = map.get(origin);if (!!copied){return copied;}   let target = {};if (Object.prototype.toString.call(origin) === "[object Array]"){target = [];}map.set(origin, target);let keys = Reflect.ownKeys(origin);for(let key of keys){   target[key] = deepClone(origin[key], map);}return target;
}
  1. 通过WeakMap记录对象是否被拷贝过,如果拷贝过,就直接将拷贝过的对象返回,不再重复克隆
  2. 如果没有被拷贝过,构建数组或普通JS对象,然后再进行记录。

克隆存在循环引用的对象,代码如下:

var obj = {a: 1};
obj.obj1 = obj;
var newObj = deepClone(obj);
console.log(newObj);

执行结果如下:

数组判定的代码优化

在克隆之前,我们必须知道要克隆的对象是一个普通JS对象还是一个数组。我们是否可以换一个思路,任何对象都有自己的构建器,如果我们直接使用构造器构造对象,也就不需要之前对象是什么类型了。

JS中所有对象都有自己的原型链,原型链中包含基类Object的构造器,如下所示:

JS普通对象

JS数组:

JS普通对象的构造方法可以构建出{},JS数组的构建方法可以构造出[]。所以如果我们调用该构造方法,就无需再去判定数组还是普通对象了。

改进后的代码如下:

function deepClone(origin, map = new WeakMap()){if (origin == undefined || typeof origin !== "object"){return origin;}if (origin instanceof Date){return new Date(origin);}var copied = map.get(origin);if (!!copied){return copied;}   let target = new origin.constructor();map.set(origin, target);let keys = Reflect.ownKeys(origin);for(let key of keys){   target[key] = deepClone(origin[key], map);}return target;
}

Javascript 深度克隆中的循环引用问题解决和代码优化相关推荐

  1. 小记 TypeScript 中的循环引用问题

    随着项目规模的不断增长,循环引用问题似乎总是不可避免,本文就 TypeScript 中可能出现的循环引用问题做了一些简单记录~ 平时编写 TypeScript 代码时,一般都倾向于使用模块(Modul ...

  2. Swift之常见闭包与defer关键字的使用分析和闭包中的循环引用 | CSDN创作打卡

    一.什么是闭包? 在 Swift 中,可以通过 func 定义一个函数,也可以通过闭包表达式定义一个函数,闭包是一个捕获了上下文的常量或者是变量的函数.闭包(Closures)是自包含的功能代码块,可 ...

  3. javascript 深度克隆对象

    js一般有两种不同数据类型的值: 基本类型(包括undefined,Null,boolean,String,Number),按值传递: 引用类型(包括数组,对象),按址传递,引用类型在值传递的时候是内 ...

  4. 覆盖所有类型的javascript深度克隆

    第一种方法(只适用于基础类型) const newObj = JSON.parse(JSON.stringify(oldObj)); 第二种方法 const getType = (obj)=> ...

  5. nstimer循环引用_ios开发中经典循环引用场景?

    1.属性传值循环引用 如:- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexP ...

  6. 【FastJSON】解决FastJson中“$ref 循环引用”的问题

    0.开发环境 SSH,EasyUI,MySQL 1.需求要求: (1)首先获取所有的贷款订单数据,即List <LoanOrder>. (2)然后从单个贷款订单实体LoanOrder去访问 ...

  7. 解决FastJson中“$ref 循环引用检测”的问题的几种方式

    一.现象: 项目中用json形式来存储一个集合对象,用fastjson发现多了一些东西:$ref,了解之后才发现是重复引用的问题. [{"id":"1",&qu ...

  8. 深入理解C++中的循环引用问题及解决方法

    循环引用问题在C++中是指当两个或多个对象互相持有对方的引用(通常是通过智能指针),导致它们的引用计数永远不会降为零,从而导致内存泄漏的情况.这种问题在使用shared_ptr时尤为突出,因为shar ...

  9. 防止iOS中私有属性在block中的循环引用

    想看答案可以直接瞅瞅底下代码. 对于一般的@property修饰的属性我们可以使用__weak转换一下self来修饰 __weak typeof(self) weakSelf = self;//然后把 ...

最新文章

  1. 浅谈 sessionStorage、localStorage、cookie 的区别以及使用
  2. 浅尝key-value数据库(三)——MongoDB的分布式
  3. php获取总共内存_php获取页面运行使用内存的两个函数
  4. 【已解决】单片机串口通讯中RXD与TXD如何连线?
  5. Refactor?or Patching?
  6. ffmpeg - AVPacket内存问题分析(AVFrame一样的)
  7. grasp设计模式应用场景_grasp设计模式笔记回顾
  8. hive建表设置如果为null_Hive表中的NULL值处理
  9. 【java】创建一个JFrame,可以使得一个字符串用按钮进行颜色的选择
  10. python获取文件名中两条下划线之间的部分_Python 中的特殊双下划线方法
  11. dubbo注入找不到bean_Dubbo配置参考手册之dubbo:reference
  12. 【粗解】【通信编码】卷积编码器的简单实现
  13. Kaggle 机器学习实战 朴素贝叶斯(原理+西瓜数据集实战)
  14. 2017年6月14日中午 java.io.IOException: Premature EOF
  15. 【转载】mac读取ntfs硬盘方法
  16. BigDecimal,BigInteger 学习以及简单示例
  17. Rockchip CAN 总线
  18. java graphics2d旋转_反向Java Graphics2D缩放和旋转坐标
  19. C语言解析wav文件格式
  20. Logisticregression学习

热门文章

  1. 快速导入Android项目
  2. 中国 GPRS接入点名称
  3. 批量给项目的java文件添加licence文件头
  4. 计算机教室cad平面图,教室的平面图怎么画
  5. SAP物料移动类型和自动科目设置(包含财务,pp) 二
  6. 在线支付系列【23】支付宝支付接入指南
  7. Linux常用命令——strace命令
  8. http请求 url 竖线_HTML 里竖线 这个“ | ” 符号怎么弄?谢谢解答。
  9. 服务器2400系列和2600系列,2400g与2600的CPU性能相差多少?最新游戏实测对比
  10. 已提交内存过大_虚拟内存别关!?!