實(shí)現(xiàn)深拷貝

1. 利用JSON實(shí)現(xiàn)

JSON.parse(JSON.stringify(obj))
問題:

  • Date()類型會(huì)變成了字符串
  • 會(huì)丟失值為undefined或函數(shù)的屬性
  • 會(huì)丟失鍵或值為Symbol類型的屬性
  • 正則會(huì)變成{}
  • 有互相嵌套的對(duì)象會(huì)報(bào)錯(cuò) Uncaught TypeError: Converting circular structure to JSON

2. 遞歸實(shí)現(xiàn)深拷貝

第一步

遇到類型為object的屬性時(shí),遞歸調(diào)用。

    function cloneDeep(obj) {
        if(obj == null){
            return obj;
        }

        const result = {};

        for(let key in obj){
            if(obj.hasOwnProperty(key)){
                if(typeof obj[key] == 'object'){
                    result[key] = cloneDeep(obj[key]);
                }else{
                    result[key] = obj[key];
                }
            }
        }

        return result;
    }

問題:

  • 對(duì)數(shù)組使用typeof操作符也會(huì)得到'object',導(dǎo)致數(shù)組會(huì)拷貝成了對(duì)象。例如[0,1,2]會(huì)變成{0:0, 1:1, 2:2}
  • for in 循環(huán)不會(huì)枚舉出Symbol類型的鍵,使得拷貝結(jié)果缺少Symbo類型鍵的屬性
  • 未處理互相嵌套的情況
第二步

處理數(shù)組

const result = Array.isArray(obj) ? [] : {};
第三步

處理Symbol類型的鍵

Object.getOwnPropertySymbols() 方法返回一個(gè)給定對(duì)象自身的所有 Symbol 屬性的數(shù)組

    const symbols = Object.getOwnPropertySymbols(obj);
    for (let s of symbols) {
        if (typeof obj[s] == 'object') {
            result[s] = cloneDeep(obj[s]);
        } else {
            result[s] = obj[s];
        }
    }

第四步

利用map處理互相嵌套的對(duì)象,當(dāng)map已經(jīng)存放了對(duì)象的時(shí)候,直接返回,不繼續(xù)遞歸下去,避免無(wú)限遞歸導(dǎo)致棧溢出。
最終版本:

    function cloneDeep(obj, map = new WeakMap()) {
        if (obj == null) {
            return obj;
        }

        if (map.has(obj)) {
            return map.get(obj);
        }

        const result = Array.isArray(obj) ? [] : {};

        map.set(obj, result);

        const symbols = Object.getOwnPropertySymbols(obj);
        for (let s of symbols) {
            if (typeof obj[s] == 'object') {
                result[s] = cloneDeep(obj[s], map);
            } else {
                result[s] = obj[s];
            }
        }

        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (typeof obj[key] == 'object') {
                    result[key] = cloneDeep(obj[key], map);
                } else {
                    result[key] = obj[key];
                }
            }
        }

        return result;
    }

3. 循環(huán)實(shí)現(xiàn)深拷貝

利用一個(gè)棧輔助進(jìn)行深度優(yōu)先遍歷。其他細(xì)節(jié)與遞歸實(shí)現(xiàn)類似。

function cloneDeep(obj) {

    const root = {};

    const stack = [{
        parent: root,
        key: undefined,
        value: obj
    }];

    const set = new WeakSet();

    while (stack.length) {
        const node = stack.pop();
        const parent = node.parent;
        const key = node.key;
        const value = node.value;

        let curNode = parent;
        if (key !== undefined) {
            //將當(dāng)前處理的節(jié)點(diǎn)和父節(jié)點(diǎn)關(guān)聯(lián)起來(lái)
            curNode = parent[key] = Array.isArray(value) ? [] : {};
        }

        if(set.has(value)){
            //如果這個(gè)對(duì)象之前已經(jīng)出現(xiàn)過(guò) 就不處理 防止無(wú)限循環(huán)
            parent[key] = value;
            continue;
        }else{
            set.add(value);
        }


        const symbols = Object.getOwnPropertySymbols(value);
        for (let s of symbols) {
            if (typeof value[s] == 'object' && value[s] != null) {
                stack.push({
                    value: value[s],
                    key: s,
                    parent: curNode
                })
            } else {
                curNode[s] = value[s];
            }
        }

        for (let k in value) {
            if (value.hasOwnProperty(k)) {
                if (typeof value[k] == 'object' && value[k] != null) {
                    stack.push({
                        parent: curNode,
                        key: k,
                        value: value[k]
                    });
                } else {
                    curNode[k] = value[k];
                }
            }
        }
    }

    return root;
}

附測(cè)試用例

    let a = {
        name: "luigi",
        book: {
            title: "hello",
            price: "10"
        },
        a1: undefined,
        a2: null,
        a3: 123,
        a4: [1, 2, 3, 4]
    }

    let s = Symbol('test');
    a[s] = 'fuck';

    var x = {
        test: a
    }
    a.x = x;

    let b = cloneDeep(a);

    a.name = "lin";
    a.book.price = "20";

    console.log(a);
    console.log(b);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容