js對象的深拷貝,你真的覺得很簡單嗎?

1.如何實現深拷貝?

1.簡單粗暴使用JSON.parse(JSON.stringify(obj))
  • 存在的問題:不支持函數和正則等拷貝。
let obj = {
            name: 'ha',
            friends: {
                name: 'zj',
                parent: {
                    name: 'parent'
                }
            },
            score: [ 1, 2, 3 ],
            getName: function () {
                return this.name;
            },
            reg: /\w/g
        }

        const obj_2 = JSON.parse(JSON.stringify(obj));

        obj_2.friends.parent.name = 12121212;
        obj_2.score.push('ja');
        console.log('obj_2==', obj_2);
        
        console.log('obj==', obj);

結果如下:
在這裏插入圖片描述

2.循環遞歸
  • 我自己的遞歸實現
// 自己實現一個對象的深拷貝
function deepClone(sourceObj) {
   if (typeof sourceObj !== 'object' || sourceObj === null) return sourceObj;
   const cloneObj = {}
   for (let key in sourceObj) {  // for...in 會遍歷對象自身的和繼承的可枚舉的屬性。
       if (sourceObj.hasOwnProperty(key)) {  // 只拷貝對象自身的屬性
           
           if (typeof sourceObj[key] !== 'object'){ // 這裏分爲兩種:1.基本類型,2.函數(我覺得無法對函數進行深拷貝了)
               cloneObj[key] = sourceObj[key];
           } else  { // 這裏分爲兩種:1.數組,2.對象 ,3.正則(正則這裏,我暫時不清楚如何深拷貝,哈哈)
               !sourceObj[key].forEach ? 
               (Object.prototype.toString.call(sourceObj[key]) !== '[object RegExp]' ? cloneObj[key] = deepClone(sourceObj[key]) : cloneObj[key] = sourceObj[key]) : 
               cloneObj[key] = [...sourceObj[key]];
           }
       }
   }
   return cloneObj;
}

判斷一個引用類型的變量是數組,對象還是正則。用instanceof或者Object.prototype.toString.call(obj)
instanceof判斷的具體代碼爲:

if (typeof obj === 'object') {
   if (obj instanceof Array) {
    // 爲數組
   } else (obj instanceof RegExp) {
   	// 爲正則
   } else {
   	// 爲對象
   }
}
  • 修言大佬的實現
function deepClone(obj) {
    // 如果是 值類型 或 null,則直接return
    if(typeof obj !== 'object' || obj === null) {
        return obj
    }
    
    // 定義結果對象
    let copy = {}
    
    // 如果對象是數組,則定義結果數組
    if(obj.constructor === Array) {
        copy = []
    }
    
    // 遍歷對象的key
    for(let key in obj) {
        // 如果key是對象的自有屬性
        if(obj.hasOwnProperty(key)) {
            // 遞歸調用深拷貝方法
            copy[key] = deepClone(obj[key])
        }
    }
    return copy
} 
  • 存在的問題:
    循環引用,即死循環,導致爆棧。
var obj = {
            name: 'ha',
            friends: {
                name: 'zj',
                parent: {
                    name: 'parent'
                }
            },
            score: [ 1, 2, 3 ],
            getName: function () {
                return this.name;
            }
        }

        obj.issue = obj;

調用deepClone(obj)後,出現如下結果。棧溢出
在這裏插入圖片描述

  • 那麼如何解決呢?
    思路:可以開闢一個數組,用來存儲已經拷貝的對象,然後,每次拷貝之前,進行判斷該對象是否已經拷貝過即可。
const hadDeepClone = []; // 存儲已經拷貝過的引用類型值
function deepClone(sourceObj) {
    if (typeof sourceObj !== 'object') return sourceObj;
    if (hadDeepClone.includes(sourceObj)) {
        return;
    } else{
        hadDeepClone.push(sourceObj);
    }
    const cloneObj = {}
    for (let key in sourceObj) {  // for...in 會遍歷對象自身的和繼承的可枚舉的屬性。
        if (sourceObj.hasOwnProperty(key)) {  // 只拷貝對象自身的屬性
            
            if (typeof sourceObj[key] !== 'object'){ // 這裏分爲兩種:1.基本類型,2.函數(我覺得無法對函數進行深拷貝了)
                cloneObj[key] = sourceObj[key];
            } else  { // 這裏分爲兩種:1.數組,2.對象 ,3.正則(正則這裏,我暫時不清楚如何深拷貝,哈哈)
                !sourceObj[key].forEach ? 
                (Object.prototype.toString.call(sourceObj[key]) !== '[object RegExp]' ? cloneObj[key] = deepClone(sourceObj[key]) : cloneObj[key] = sourceObj[key]) : 
                cloneObj[key] = [...sourceObj[key]];
            }
        }
    }
    return cloneObj;
}

結果如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章