JavaScript:深拷貝和淺拷貝區別,以及實現深拷貝的方法


區別:

深拷貝和淺拷貝最根本的區別在於是否是真正獲取了一個對象的復制實體,而不是引用,

深拷貝在計算機中開辟了一塊內存地址用於存放復制的對象,而淺拷貝僅僅是指向被拷貝的內存地址,如果原地址中對象被改變了,那么淺拷貝出來的對象也會相應改變。

實現深拷貝的方法:

  • 最簡單的方法就是JSON.parse(JSON.stringify())

需要注意的是:這種拷貝方法不可以拷貝一些特殊的屬性(例如正則表達式,undefine,function)

  • 用遞歸去復制所有層級屬性
function deepCopyTwo(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if (obj && typeof obj == 'object') {
        for (const key in obj) {
            //判斷obj子元素是否為對象,如果是,遞歸復制
            if (obj[key] && typeof obj[key] === "object") {
                objClone[key] = deepCopyTwo(obj[key]);
            } else {
                //如果不是,簡單復制
                objClone[key] = obj[key];
            }
        }
    }
    return objClone;
}
export const shallowClone = (object: any) => {
    const newObject = {};
    for (const key in object) {
        if (object.hasOwnProperty(key)) {
            newObject[key] = object[key];
        }
    }
    return newObject;
}

export const jsonClone = (object: any) => {
    return JSON.parse(JSON.stringify(object));
}

export const cloneDeep = (object: any) => {
    if (typeof object !== "object") return object;
    const newObject = object instanceof Array ? [] : {};
    for (const key in object) {
        if (object.hasOwnProperty(key)) {
            if (typeof object[key] === "object" && object[key] !== null) {
                newObject[key] = cloneDeep(object[key]);
            } else {
                newObject[key] = object[key];
            }
        }
    }
    return newObject
}

// 對特殊對象進行類型判斷
const isType = (object, type): boolean => {
    if (typeof object !== "object") return false;
    const typeString = Object.prototype.toString.call(object);
    switch (type) {
        case "Array":
            return typeString === "[object Array]";
        case "Date":
            return typeString === "[object Date]";
        case "RegExp":
            return typeString === "[object RegExp]";
        default:
            return false;
    }
}

// 實現一個提取flags的函數
const getRegExpFlags = (regExp: RegExp) => {
    let flags = "";
    if (regExp.global) flags += "g";
    if (regExp.ignoreCase) flags += "i";
    if (regExp.multiline) flags += "m";
    return flags;
}
// Buffer對象、Promise、Set、Map暫未處理
export const deepClone = oldObject => {
    // 維護兩個儲存循環引用的數組
    const oldObjects = [];
    const newObjects = [];

    const _deepClone = oldObject => {
        // 遞歸直到oldobject為null時,或類型不為“object”時停止。
        if (oldObject === null) return null;
        if (typeof oldObject !== 'object') return oldObject; // 

        let newObject, newProtoType;

        if (isType(oldObject, 'Array')) {
            /**
             * 對數組做特殊處理
             * 數組里面如果單純只有多個基本數據類型將在判斷是否時object對象時進行return
             * 比如數組[1,4,6]
             * forin循環里key為數組下標,進行循環復制,都止步於前面兩個return,此時的循環不會進行定義新newObject
             * new Array(2) [undefined, undefined],下面的forin循環不會對這個Array進行循環,
             * 所以最后拷貝結果是[]不是[undefined, undefined]
             */
            newObject = [];
        } else if (isType(oldObject, 'RegExp')) {
            // 對正則對象做特殊處理,下面的newObject[key] = 此次返回的newObject
            newObject = new RegExp(oldObject.source, getRegExpFlags(oldObject));
            if (oldObject.lastIndex) newObject.lastIndex = oldObject.lastIndex;
        } else if (isType(oldObject, 'Date')) {
            // 對Date對象做特殊處理
            newObject = new Date(oldObject.getTime());
        } else {
            // 處理對象原型
            newProtoType = Object.getPrototypeOf(oldObject);
            // 利用Object.create切斷原型鏈
            newObject = Object.create(newProtoType);
        }

        // 處理循環引用
        const index = oldObjects.indexOf(oldObject);

        if (index != -1) {
            // 如果父數組存在本對象,說明之前已經被引用過,直接返回此對象
            return newObjects[index];
        }
        oldObjects.push(oldObject);
        newObjects.push(newObject);

        for (const key in oldObject) {
            if (oldObject.hasOwnProperty(key)) {
                // newObject 已根據條件做了特殊處理
                newObject[key] = _deepClone(oldObject[key]);
            }
        }

        return newObject;
    };
    return _deepClone(oldObject);
};

 

淺拷貝:object.assign(target,source)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM