javascript對深拷貝物件的研坑

張炳發表於2019-02-16

對於深拷貝,淺拷貝的概念不多說,概念可以自行百度喲!這裡對深拷貝物件進行一些研究!

只有值型別資料的深拷貝

針對只有值的資料物件,下面一行程式碼足以!

JSON.parse(JSON.stringify(obj))

不嚴謹的簡單的深拷貝

function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            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;
}

問題存在:

  • 拷貝環,也就是對 物件迴圈引用 的拷貝出現問題

針對環的深拷貝

可以使用一個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);

更多遺留問題,針對函式進行拷貝,若是function,非箭頭函式,如何解決?還有,若要拷貝原型鏈上的屬性?如何拷貝不可列舉屬性? 如何拷貝Error物件等等的坑?

相關文章