实现深拷贝

作者: Luigi_Lin | 来源:发表于2019-02-21 00:36 被阅读0次

    1. 利用JSON实现

    JSON.parse(JSON.stringify(obj))
    问题:

    • Date()类型会变成了字符串
    • 会丢失值为undefined或函数的属性
    • 会丢失键或值为Symbol类型的属性
    • 正则会变成{}
    • 有互相嵌套的对象会报错 Uncaught TypeError: Converting circular structure to JSON

    2. 递归实现深拷贝

    第一步

    遇到类型为object的属性时,递归调用。

        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;
        }
    

    问题:

    • 对数组使用typeof操作符也会得到'object',导致数组会拷贝成了对象。例如[0,1,2]会变成{0:0, 1:1, 2:2}
    • for in 循环不会枚举出Symbol类型的键,使得拷贝结果缺少Symbo类型键的属性
    • 未处理互相嵌套的情况
    第二步

    处理数组

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

    处理Symbol类型的键

    Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组

        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处理互相嵌套的对象,当map已经存放了对象的时候,直接返回,不继续递归下去,避免无限递归导致栈溢出。
    最终版本:

        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. 循环实现深拷贝

    利用一个栈辅助进行深度优先遍历。其他细节与递归实现类似。

    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) {
                //将当前处理的节点和父节点关联起来
                curNode = parent[key] = Array.isArray(value) ? [] : {};
            }
    
            if(set.has(value)){
                //如果这个对象之前已经出现过 就不处理 防止无限循环
                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;
    }
    

    附测试用例

        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);
    

    相关文章

      网友评论

        本文标题:实现深拷贝

        本文链接:https://www.haomeiwen.com/subject/anclyqtx.html