這次讓面試官非常滿意:手撕深拷貝[15行]

這次讓面試官非常滿意:手撕深拷貝

————-人工分割線————-

淺拷貝這裡不做介紹了,深拷貝的實現要點,除了一定要深!還要能兼容各種類型,如函數,正則、Date等等。
其實日常業務開發中,Json.parse(Json.stringfy(obj))已經能夠解決90%左右的克隆需求。還有Object.assign() Object.create()都能滿足一定的克隆需求。

但上班擰螺絲,面試造航母的精神不容小覷!本着專研精神去看待怎麼才能寫個牛逼、各種場合都能通用的克隆方式?我做了不少功課。

在思考如何實現深拷貝之前,首先得弄明白:

  • 如何遍歷對象?點這可以傳送
  • 如何判斷數據類型?點這可以傳送
  • 如何創建對象?【由於懶沒寫】
  • 如何克隆函數,正則、Date對象?
  • 如何解決循環引用問題?

實現思路:

  • 使用遞歸實現嵌套對象的遍歷
  • 增加WeakMap來緩存已經創建的克隆對象解決循環引用問題
  • 區分不同數據類型的處理方式,增加通用類型集合來存儲通用類型,方便日後拓展

直接上代碼:

待克隆對象

let obj = {
        newDate: new Date('2020-10-11 12:00:01'),
        null: null,
        string: 'string',
        num: 23,
        nan: NaN,
        func: function () {console.log('function')},
        arr: [1,2,3],
        reg: /ABC\-001/,
        regObj: new RegExp(/ABC/),
        object: {
            c: {
                a: 1
            }
        },
    };
    obj.loop = obj; // 循環引用

    let sup = {
        name: 'super',
        show: function () {
            console.log('show')
        }
    }
    let objectSup = {
        objectSupName: 'objectSupName',
        objectSupShow: function () {
            console.log('objectSupShow')
        }
    }
    Object.setPrototypeOf(obj, sup); // 根對象設置原型對象
    Object.setPrototypeOf(obj.object, objectSup); // 子對象設置原型對象

核心代碼

  	// 使用WeakMap是便於回收
    function deepClone(obj, cacheCurr = new WeakMap()) {
        // 如果該對象已經創建好,則從緩存中獲取直接返回
        if (cacheCurr.has(obj)) return cacheCurr.get(obj);
        // 通用的類型集合,方便後面統一處理:new obj.constructor(obj),所以該集合一定是要能夠這樣創建的才能放進來
        const types = ['RegExp', 'Date', 'Set', 'Map', 'WeakMap', 'WeakSet'];
        // 獲取當前對象類型
        let objDataType = Object.prototype.toString.call(obj).slice(8, -1);
        // 對比當前類型是否在通用類型中,在則統一處理克隆。【較通用的處理方式】
        if(types.includes(objDataType)) return new obj.constructor(obj);
        // 創建克隆對象
        let cloneObj = objDataType === 'Array' ? [] : {};
        // 繼承原型
        if(obj) Object.setPrototypeOf(cloneObj,Object.getPrototypeOf(obj));
        // 普通引用類型及非引用類型克隆,Reflect.ownKeys能夠獲取自身所有屬性【非枚舉也可】
        for(let key of Reflect.ownKeys(obj)) {
            let value = obj[key];
            let valueType = Object.prototype.toString.call(value).slice(8, -1);
            // 引用類型處理
            if(valueType === 'Object' || valueType === 'Array' || types.includes(valueType)) {
            	// 對引用類型進行遞歸進入當前級的下一級進行遍歷
                cloneObj[key] = deepClone(value, cacheCurr);
                // 記錄已創建引用
                cacheCurr.set(obj, cloneObj);
            } else { // 非引用類型處理
                cloneObj[key] = value;
            }
        }
        return cloneObj;
    }

代碼注釋感覺都寫的很清楚了。寫完這篇文章就可以安心的來一把無限火力了。

測試過挺多數據結構的都完美通過。如果有發現問題的同學幫忙指出來。感激不盡