如何深度克隆一個對象

如何深度克隆一個對象

在咱們平常工做中常常會遇到須要去克隆一個對象好比多個地方用到的公共的圖表基本參數的配置

相信不少人會想到用 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;

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

相關文章
相關標籤/搜索