深拷贝和浅拷贝

265 阅读3分钟

参考文章: www.cnblogs.com/echolun/p/7…

www.jianshu.com/p/2188dcd91…

由于基本数据类型存储在栈内存中,

他们有各自不同的存储空间,当其中一方发生变化时,另一方并不会发生改变

而引用数据类型的名存储在栈内存,而值存储在堆内存中,当我们使用引用数据类型时实际使用的是栈内存提供的引用地址指向堆内存中的值,

那么当我们引用的值发生变化时,我们本身会发生变化吗?

var a = ['1','2'];
    var b = a;
    console.log(b);
    a.push('3');
    console.log(b);

实际上是会的,因为他们指向的同一个地址,这就是浅拷贝

当我们在实际使用时,浅拷贝会给我们带来很多问题,那么深拷贝怎么实现呢?

思路1:递归复制所有属性层级

function deepcopy1(obj) {
        let result = Array.isArray(obj)?[]:{};
        if(obj && typeof obj === 'object'){
            for(let key in obj){
                if(obj.hasOwnProperty(key)){
                    if(obj[key] && typeof obj[key] === 'object'){
                        result[key] = deepcopy1(obj[key]);
                    }
                    else{
                        result[key] = obj[key]
                    }
                }
            }
        }
        return result;
    }
    var a = ['1','2',{name:'tina',sex:'female'},'4',{data:[1,2,3,{key:'1'}],msg:'hhhh'}];
    var b = deepcopy1(a);
    var c = JSON.parse(JSON.stringify(a));

    a.push('2');
    a[4].data.push('2');
    console.log(a);
    console.log(b);

问题:

a[10] = a;

当对象中有循环引用时,会报错

思路2:利用js自带的深拷贝方法

    var a = ['1','2',{name:'tina',sex:'female'},'4',{data:[1,2,3,{key:'1'}],msg:'hhhh'}];
    var c = JSON.parse(JSON.stringify(a));

    a.push('2');
    a[4].data.push('2');
    console.log(a);
    console.log(c);

也是可同样的效果

问题: 1.当要深拷贝的对象中有undefined和函数时,拷贝不成功,会出现null

2.当对象中有循环引用时,会报错

思路3:在思路1的方法中检验一下一个对象的字段是否引用了这个对象或这个对象的任意父级

function deepcopy2(obj,parent = null) {
        let result = Array.isArray(obj)?[]:{};
        let _parent = parent;
        while(_parent){
            _parent.originalPolicy
            if(_parent.originalParent === obj){
                return _parent.currentParent;
            }
            _parent = _parent.parent;
        }
        if(obj && typeof obj === 'object'){
            for(let key in obj){
                if(obj.hasOwnProperty(key)){
                    if(obj[key] && typeof obj[key] === 'object'){
                        result[key] = deepcopy1(obj[key],{
                            originaParent:obj,
                            currentParent:result,
                            parent:parent
                        });
                    }
                    else{
                        result[key] = obj[key]
                    }
                }
            }
        }
        return result;
    }
    var a = ['1','2',{name:'tina',sex:'female'},'4',{data:[1,2,3,{key:'1'}],msg:'hhhh'},undefined,deepcopy1];
    var b = deepcopy2(a);
    a[10] = a;
    a.push('2');
    a[4].data.push('2');
    console.log(a);
    console.log(b);

思路4:深拷贝函数最终版(支持基本数据类型、原型链、RegExp、Date类型)

function deepcopy3(obj, parent = null) {
        let result; // 最后的返回结果

        let _parent = parent; // 防止循环引用
        while(_parent){
            if(_parent.originalParent === obj){
                return _parent.currentParent;
            }
            _parent = _parent.parent;
        }

        if(obj && typeof obj === "object"){ // 返回引用数据类型(null已被判断条件排除))
            if(obj instanceof RegExp){ // RegExp类型
                result = new RegExp(obj.source, obj.flags)
            }else if(obj instanceof Date){ // Date类型
                result = new Date(obj.getTime());
            }else{
                if(obj instanceof Array){ // Array类型
                    result = []
                }else{ // Object类型,继承原型链
                    let proto = Object.getPrototypeOf(obj);
                    result = Object.create(proto);
                }
                for(let key in obj){ // Array类型 与 Object类型 的深拷贝
                    if(obj.hasOwnProperty(key)){
                        if(obj[key] && typeof obj[key] === "object"){
                            result[key] = deepcopy3(obj[key],{
                                originalParent: obj,
                                currentParent: result,
                                parent: parent
                            });
                        }else{
                            result[key] = obj[key];
                        }
                    }
                }
            }
        }else{ // 返回基本数据类型与Function类型,因为Function不需要深拷贝
            return obj
        }
        return result;
    }
    // 调试用
    function construct(){
        this.a = 1,
            this.b = {
                x:2,
                y:3,
                z:[4,5,[6]]
            },
            this.c = [7,8,[9,10]],
            this.d = new Date(),
            this.e = /abc/ig,
            this.f = function(a,b){
                return a+b
            },
            this.g = null,
            this.h = undefined,
            this.i = "hello",
            this.j = Symbol("foo")
    }
    construct.prototype.str = "I'm prototype"
    var obj1 = new construct()
    obj1.k = obj1
    obj2 = deepcopy3(obj1)
    obj2.b.x = 999
    obj2.c[0] = 666
    console.log(obj1)
    console.log(obj2)
    console.log(obj1.str)
    console.log(obj2.str)