如何深度克隆一个对象

如何深度克隆一个对象

在咱们平常工做中常常会遇到须要去克隆一个对象好比多个地方用到的公共的图表基本参数的配置

相信不少人会想到用 Object.assign, JSON.stringifyJSON.parse 方法去克隆一个对象,这个能够明确告诉你们这些都是些不靠谱的浅度克隆。javascript

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

clipboard.png

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

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

clipboard.png

你们有没有发现什么异常?虽然 JSON.stringify(value[, replacer[, space]]) 能够处理可是太麻烦了,这个方法我就很少说了具体仍是参考文档
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringifysegmentfault

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

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;
}

看是靠谱是真是假咱们来验证一把 仍是在控制台输入数组

clipboard.png

你们发现什么异常的了吗?浏览器

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

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

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

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

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] 对象了原理都是同样

各类状况分析完了才说算是真克隆
咱们在控制台看下3d

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

clipboard.png

是否是解决了? 在此并无结束。 专一的伙伴们相信发现了对象中包含了个 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});

在看控制台

clipboard.png

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

完整代码以下

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;

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

相关文章
相关标签/搜索