Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何实现一个稳定的深拷贝? #4

Open
Diazhao opened this issue Feb 12, 2019 · 1 comment
Open

如何实现一个稳定的深拷贝? #4

Diazhao opened this issue Feb 12, 2019 · 1 comment

Comments

@Diazhao
Copy link
Owner

Diazhao commented Feb 12, 2019

深拷贝在日常编码中经常用到,用哪种方式实现的深拷贝最为稳定呢?

@Diazhao
Copy link
Owner Author

Diazhao commented Feb 12, 2019

  1. 第一种方式,最为简单常用。通过JSON字符串的编译与解析即可。
JSON.parse(JSON.stringify(object))

这种方式实现可以满足基本9成以上的对象拷贝。但是却无法处理带有循环引用的对象,例如
let aa = {}; aa.a = aa, 要拷贝这种情况的对象,使用这种方法会提示栈溢出。

  1. 第二种方式,一种简单的递归调用
function deepCopy(obj, hash = new WeakMap()){
    if(!isObject(obj)) return obj;
    if(hash.has(obj)) return hash.get(obj);

    var target = Array.isArray(obj) ? [] : {};
    hash.set(obj, target);

    for(var key in obj){
        if(isObject(obj[key])){
            target[key] = deepCopy(obj[key], hash)
        }else{
            target[key] = obj[key]
        }
    }

    return target;
}
function isObject(obj){
    return typeof obj === 'object' && obj !== null;
}

上面的方式考虑到了数组的数组以及值为null时的情况。而且通过使用一个WeakMap来解决了循环引用的问题。但是这种方式的缺点就是对象层级较深的情况下,同样的会由于递归造成递归爆栈的问题。而且,这种方式使用了WeakMap,对ES5的兼容性也不佳。
考虑到兼容性的问题,这里可以将WeakMap用数组来实现。但是注意到,如果使用数组的话,每次复制前都需要在数组中遍历,去寻找对应的对象是否已经在数组中存在过。这样会造成性能的问题,在处理数据量较大的情况下,会有延时。
考虑到递归爆栈的问题,可以将递归修改为循环来解决。

  1. 循环便利一个对象,实质就是编译一颗树。实现方式如下,采用深度优先便利的形式
function deepCopy2(obj){
    var root = {};
    var loopStack = [
        {
            parent: root,
            data: obj,
            key: undefined
        }
    ]

    while(loopStack.length){
        var node = loopStack.pop();
        var parent = node.parent;
        var data = node.data;
        var key = node.key;

        var res = parent;
        if(typeof key !== "undefined"){
            if(Array.isArray(data)){
                res = parent[key] = [];
            }else{
                res = parent[key] = {};
            }
            
        }
        for(var k in data){
            if(isObject(data[k])){
                loopStack.push({
                    parent: res,
                    key: k,
                    data: data[k]
                })
            }else{
                res[k] = data[k]
            }
        }
    }

    return root;
}

以上便是常用的几种深拷贝的实现方式,各有特点,需要根据不同的需要选择不同的实现方式。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant