如何深度克隆一个对象

韩滔
• 阅读 6536

如何深度克隆一个对象

在我们日常工作中经常会遇到需要去克隆一个对象比如多个地方用到的公共的图表基本参数的配置

相信很多人会想到用 Object.assign, JSON.stringifyJSON.parse 方法去克隆一个对象,这个可以明确告诉大家这些都是些不靠谱的浅度克隆。

我们先来试一下 Object.assign 在控制台执行下列操作

如何深度克隆一个对象

大家有没有发现联动了。关于此方法具体请参考文档
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

接下来我们看下 JSON.stringifyJSON.parse 克隆对象,同样在控制输入

如何深度克隆一个对象

大家有没有发现什么异常?虽然 JSON.stringify(value[, replacer[, space]]) 可以处理但是太麻烦了,这个方法我就不多说了具体还是参考文档
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

下面咋们来看一种稍微靠谱的一种方式。在本站搜的前几条中发现的。

function isArray (arr) {
    return Object.prototype.toString.call(arr) === '[object Array]';  
}
// 深度克隆
function deepClone (obj) {  
    if(typeof obj !== "object" && typeof obj !== 'function') {
        return obj;        //原始类型直接返回
    }
    var o = isArray(obj) ? [] : {}; 
    for(i in obj) {  
        if(obj.hasOwnProperty(i)){ 
            o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]; 
        } 
    } 
    return o;
}

看是靠谱是真是假我们来验证一把 还是在控制台输入

如何深度克隆一个对象

大家发现什么异常的了吗?

这个没有区分具体的对象,在此问下大家js的对象有哪些呢?相信一般人答不出来4个
[object Object], [object Array], [object Null], [object RegExp], [object Date], [object HTMLXXElement], [object Map],[object Set],... 等等一系列

检测类型使用 Object.prototype.toString.call(xxx)typeof

我们分析下上面对象中哪些是引用类型需要特殊处理呢?相信大家都不陌生了。[object Object][object Array]

好!详细大家思路有了,咋们用递归来实现一把吧!

const deepClone = function(obj) {
  // 先检测是不是数组和Object
  // let isMap = Object.prototype.toString.call(obj) === '[object Map];
  // let isSet = Object.prototype.toString.call(obj) === '[object Set];
  // let isArr = Object.prototype.toString.call(obj) === '[object Array]';
  let isArr = Array.isArray(obj);
  let isJson = Object.prototype.toString.call(obj) === '[object Object]';
  if (isArr) {
    // 克隆数组
    let newObj = [];
    for (let i = 0; i < obj.length; i++) {
      newObj[i] = deepClone(obj[i]);
    }
    return newObj;
  } else if (isJson) {
    // 克隆Object
    let newObj = {};
    for (let i in obj) {
      newObj[i] = deepClone(obj[i]);
    }
    return newObj;
  }
  // 不是引用类型直接返回
  return obj;
};

Object.prototype.deepClone = function() {
  return deepClone(this);
};
咋们先不考虑Map Set Arguments [object XXArrayBuffer] 对象了原理都是一样

各种情况分析完了才说算是真克隆
我们在控制台看下

  • 注意先要把方法在控制台输进去,在调试

如何深度克隆一个对象

是不是解决了? 在此并没有结束。 专注的伙伴们相信发现了对象中包含了个 deepClone 方法,具体细节我们在此就不多说了,我们给 Object 添加了个 Object.prototype.deepClone方法导致了每个对象都有了此方法。

原则上我们不允许在原型链上添加方法的,因为在循环中 for in, Object.entries, Object.values, Object.keys 等方法会出现自定义的方法。

相信熟悉 Object 文档的伙伴人已经知道解决方案了,

Object.defineProperty 这个方法给大家带来了福音 具体参考 Object 文档。我们使用一个enumerable (不可枚举)属性就可以解决了。

在原来基础上添加以下代码即可。

Object.defineProperty(Object.prototype, 'deepClone', {enumerable: false});

在看控制台

如何深度克隆一个对象

同样上面方法中也是无法克隆一个不可枚举的属性。

完整代码如下

const deepClone = function(obj) {
  // 先检测是不是数组和Object
  // let isArr = Object.prototype.toString.call(obj) === '[object Array]';
  let isArr = Array.isArray(obj);
  let isJson = Object.prototype.toString.call(obj) === '[object Object]';
  if (isArr) {
    // 克隆数组
    let newObj = [];
    for (let i = 0; i < obj.length; i++) {
      newObj[i] = deepClone(obj[i]);
    }
    return newObj;
  } else if (isJson) {
    // 克隆Object
    let newObj = {};
    for (let i in obj) {
      newObj[i] = deepClone(obj[i]);
    }
    return newObj;
  }
  // 不是引用类型直接返回
  return obj;
};

Object.prototype.deepClone = function() {
  return deepClone(this);
};
Object.defineProperty(Object.prototype, 'deepClone', {enumerable: false});
为了兼容低版本浏览器需要借助 babel-polyfill;

最后还是推荐lodash的cloneDeep方法。

好了,深度克隆介绍到此。

点赞
收藏
评论区
推荐文章
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Python进阶者 Python进阶者
3年前
一文解读JavaScript中的文档对象(DOM)
大家好,我是IT共享者,人称皮皮。前言相信做网站对JavaScript再熟悉不过了,它是一门脚本语言,不同于Python的是,它是一门浏览器脚本语言,而Python则是服务器脚本语言,我们不光要会Python,还要会JavaScript,因为它对做网页方面是有很大作用的。1.文档对象(DOM)1).Document对象这是我们用的最普遍的一个文档对象了
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
灯灯灯灯 灯灯灯灯
4年前
「超全超细」Java设计模式图文详解!!!
java设计模式—原型模式Java原型模式1、概述  啥是原型模式?  原型模式属于设计模式中的创建型中的一员,  原型模式:使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象!  说大白话就是自己复制自己,通过原生对象复制出一个新的对象,这两个对象结构相同且相似;  需要注意的是,原型对象自己不仅是个对象还是个工厂!并且通过克隆方式创
Wesley13 Wesley13
3年前
Java深拷贝和浅拷贝
1.浅复制与深复制概念⑴浅拷贝(浅克隆)   复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。⑵深拷贝(深克隆)   复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,而不再是原有的那些被引用的对象。换言之,深复制
Stella981 Stella981
3年前
JavaScript的 基本数据类型
第一:Javascript对象是第二:Javascript中第三:Javascript的对象是数据;第四:JavaScript中的对象可以简单理解成"名称:值"对(name:value)。名称(name):"名称"部分是一个JavaScript字符串参考https://www
Wesley13 Wesley13
3年前
Java中如何克隆集合——ArrayList和HashSet深拷贝
编程人员经常误用各个集合类提供的拷贝构造函数作为克隆List,Set,ArrayList,HashSet或者其他集合实现的方法。需要记住的是,Java集合的拷贝构造函数只提供浅拷贝而不是深拷贝,这意味着存储在原始List和克隆List中的对象是相同的,指向Java堆内存中相同的位置。增加了这个误解的原因之一是对于不可变对象(https:/
Wesley13 Wesley13
3年前
unity 无限循环2D游戏原理与实践
一,目录二,实现原理1,无限循环游戏都有一个特点,就是动态产生和销毁游戏场景。在unity中,动态克隆一个物体是使用Instantiate()来产生一个物体,动态的放置在场景中。销毁一个物体使用Destroy()。2,Instantiate()是一个比较消耗资源的方法,在游戏运作中大量克隆和销毁物体,将降低游戏性能,甚至卡壳。在实际开发中,使用的
Wesley13 Wesley13
3年前
Java中深度克隆和浅度克隆
一:使用目的:就是为了快速构造一个和已有对象相同的副本。如果需要克隆对象,一般需要先创建一个对象,然后将原对象中的数据导入到新创建的对象中去,而不用根据已有对象进行手动赋值操作。二:Object中的clone()方法protectednativeObjectclone(
Wesley13 Wesley13
3年前
Java原型模式
原型模式  原型模式也称克隆模式。原型模式jianmingzhiyi,就是先创造出一个原型,然后通过类似于Java中的clone方法,对对象的拷贝,克隆类似于new,但是不同于new。new创造出来的对象采用的是默认值。克隆出来的对象与原型对象相同,同时不会影响原型对象,然后在修改克隆出来的对象。实现  继承Cloneable接口,重写cl
JXDN JXDN
1年前
你写的深度克隆真的“深度”吗?
为了更好的理解其原理,我们自己来手写一个通用性非常强的深度克隆函数
韩滔
韩滔
Lv1
层楼望,春山叠;家何在?
文章
3
粉丝
0
获赞
0