再說深拷貝對象的研坑

淺拷貝:只複製指向某個對象的指針,而不復制對象自己,新舊對象仍是共享同一塊內存。
深拷貝:會另外創造一個如出一轍的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。數組

淺拷貝的實現方式

直接賦值一個變量

let obj = {username: 'kobe', age: 39, sex: {option1: '男', option2: '女'}};
  let obj1 = obj;
  obj1.sex.option1 = '不男不女'; // 修改複製的對象會影響原對象
  console.log(obj1, obj);

alt text

Object.assign()

let obj = {
    username: 'kobe'
    };
let obj2 = Object.assign(obj);
obj.username = 'wade';
console.log(obj2);//{username: "wade"}

Array.prototype.concat()

let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);

修改新對象會改到原對象:函數

alt text

Array.prototype.slice()

let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

一樣修改新對象會改到原對象:spa

alt text

關於Array的slice和concat方法的補充說明:Array的slice和concat方法不修改原數組,只會返回一個淺複製了原數組中的元素的一個新數組。詳細規則請看MDN對應函數講解。prototype

深拷貝的實現方式

只有值類型數據的深拷貝

針對只有值的數據對象,下面一行代碼足以!指針

JSON.parse(JSON.stringify(obj))

原理: 用JSON.stringify將對象轉成JSON字符串,再用JSON.parse()把字符串解析成對象,一去一來,新的對象產生了,並且對象會開闢新的棧,實現深拷貝。這種方法只能處理只有值類型數據的拷貝。code

不嚴謹的簡單的深拷貝

function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
             //經過hasOwnProperty方法來進行篩選,全部繼承了 Object 的對象都會繼承到 hasOwnProperty 方法。這個方法能夠用來檢測一個對象是否含有特定的自身屬性;和 in 運算符不一樣,該方法會忽略掉那些從原型鏈上繼承到的屬性。
            if (typeof source[i] === 'object') {
                target[i] = clone(source[i]); // 注意這裏
            } else {
                target[i] = source[i];
            }
        }
    }

    return target;
}

問題存在:對象

  • 沒有對參數作檢驗
  • 判斷是否對象的邏輯不夠嚴謹
  • 沒有考慮數組的兼容

進階深拷貝

function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}

function deepCopy(obj)
{
    let tempObj = Array.isArray(obj) ? [] :{};
    for(let key in obj)
    {
        tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key];
    }
    return tempObj;
}

問題存在:blog

  • __拷貝環__,也就是對 對象循環引用 的拷貝出現問題

對象環知識點補充

方法一 用try catch的捕獲異常的方法來判斷,代碼簡潔繼承

function cycleDetector (obj) {
    console.log(arguments)
  // 請添加代碼
    let result = false;
    try {
        JSON.stringify(obj);
    } catch (e) {
        result = true;
    } finally {
        return result;
    }
}

方法二 時間更快,可是它執行遞歸,邏輯較第一種更復雜,空間也須要更大遞歸

function cycleDetector2(obj) {
    let hasCircle = false,
        cache = [];

    (function(obj) {
        Object.keys(obj).forEach(key => {
            const value = obj[key]
            if (typeof value == 'object' && value !== null) {
                const index = cache.indexOf(value)
                if (index !== -1) {
                    hasCircle = true
                    return
                } else {
                    cache.push(value)
                    arguments.callee(value)
                    // (注:箭頭函數沒有arguments對象,此時的arguments指向該匿名函數的參數)
                }
            }
        })
    })(obj)

    return hasCircle
}

針對環的深拷貝

能夠使用一個WeakMap結構存儲已經被拷貝的對象,每一次進行拷貝的時候就先向WeakMap查詢該對象是否已經被拷貝,若是已經被拷貝則取出該對象並返回,將deepCopy函數改形成以下:

function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {    
    if(hash.has(obj)) return hash.get(obj)    
    let cloneObj = Array.isArray(obj) ? [] : {}
    hash.set(obj, cloneObj)    
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }   
    return cloneObj
}

問題存在:

  • 沒有考慮對new Date(),正則,函數類型的對象的拷貝

結合環,針對date,reg,箭頭函數類型的深拷貝

const obj = {    arr: [111, 222],    obj: {key: '對象'},    a: () => {console.log('函數')},    date: new Date(),    reg: /正則/ig}

function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {  
      let cloneObj;   
      let Constructor = obj.constructor; 
      switch(Constructor){       
        case RegExp:
            cloneObj = new Constructor(obj)         
            break;
        case Date:
            cloneObj = new Constructor(obj.getTime())           
            break;
        case Function:
            cloneObj = eval(obj.toString());
            break;
        default:           
         if(hash.has(obj)) return hash.get(obj)
            cloneObj = new Constructor()
            hash.set(obj, cloneObj)
      }   

      for (let key in obj) {
            cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
      }    
      return cloneObj;
}

const cloneObj = deepCopy(obj);
console.log(cloneObj);
相關文章
相關標籤/搜索