JavaScript中对象的深度克隆

JS中的对象

js中的数据分为两大类:
原始类型对象类型

  • 原始类型:
    1. 数值
    1. 字符串
    1. 布尔值
    1. Null
    1. Undefined

    1. ECMA6里面还多了个Symbol,不太了解的说

  • 对象类型
    1. 函数(js中的一等对象)
    1. 数组(键值的有序集合)
    1. 其他

    既然数据类型分为两类,这两类在复制克隆时是有很大区别的。原始类型存储的是对象的实际数据,而对象类型存储的是对象的引用地址(对象的实际内容单独存放,为了减少数据开销通常存放在内存中)
    ps:对象的原型也是引用对象,它把原型的方法和属性放在内存当中,通过原型链的方式来指向这个内存地址。

    克隆的概念

    浅度克隆:原始类型为值传递,对象类型仍为引用传递
    深度克隆:所有属性或元素均完全复制,与原对象完全脱离,也就是说对于新对象的修改都不会反映到原对象中。

    浅克隆的表现

  • 1.原始类型
  • //数值克隆
    var a="1";
    var b=a;
    b="2";
    console.log(a);//"1"
    console.log(b);//"2"
    //字符串克隆
    var c="1";
    var d=c;
    d="2";
    console.log(c);//"1"
    console.log(d);//"2"
    //Boolean克隆
    var x=true;
    var y=x;
    y=false;
    console.log(x);//"true"
    console.log(y);//"false"
    

    原始类型即使我们采用普通的克隆方式仍能得到正确的结果,原因就是原始类型存储的是对象的实际数据。

  • 对象类型
  • 前面说过,函数是一等对象,当然也是对象类型,但是函数的克隆通过浅克隆即可实现。

    var m=function(){
        alert(1);
    }
    var n=m;
    n=function(){
        alert(2);
    }
    console.log(m());
    console.log(n());
    直接通过普通赋值的方式,就实现了函数的克隆,并且不会影响之前的对象。原因就是函数的克隆会在内存单独开辟一块空间,互不影响。 现在定义一个复杂的对象类型oPerson。下面看一下对象类型的浅复制有什么危害:
    var oPerson={
        oName:"rookiebob",
        oAge:"18",
        oAddress:{
            province:"beijing"
        },
        oFavorite:["swimming",{
            reading:"history book"
        }],
        skill:function(){
            console.log("bob is coding");
        }
    };
    
    function clone(obj){
        var result = {};
        for(key in obj){
            result[key] = obj[key];
        }
        return result;
    }
    var oNew = clone(oPerson);
    console.log(oPerson.oAddress.province);//beijing
    oNew.oAddress.province="shanghai";
    console.log(oPerson.oAddress.province);//shanghai
    

    经过对象克隆以后,我修改oNew的地址,发现原对象oPerson也被修改了。这说明对象的克隆不够彻底,那也就是说深度克隆失败!

    深度克隆的实现

    为了保证对象的所有属性都被复制到,我们必须知道,如果for循环以后,得到的元素仍是Object或者Array,那么需要再次循环,知道元素师原始类型或者函数为止。为了得到元素的类型,我们定义了一个通用函数,用来返回传入对象的类型。

    function isClass(o){
        if (o===null) {return Null;};
        if (o===undefined) {return Undefined;};
         return Object.prototype.toString.call(o).slice(8,-1);
    }
    //Object.prototype.toString.call()能直接返回对象的类属性,形如"[object class]"的字符串,我们通过截取class,就能知道传入的对象是什么类型
    //深度克隆
    function deepClone(obj){
        var result,oClass=isClass(obj);
        //确定result的类型
        if (oClass==="Object") {
            result={};
        }else if(oClass==="Array"){
            result=[];
        }else{
            return obj;
        }
    
        for(key in obj){
            var copy = obj[key];
            if (isClass(copy)=="Object") {
                result[key]=arguments.callee(copy);//递归调用1
            }else if (isClass(copy)=="Array") {
                result[key]=arguments.callee(copy);
            }else{
                result[key]=obj[key];
            }
        }
        return result;
    }
    
    var oNew=deepClone(oPerson);
    oNew.oFavorite[1].reading="picture";
    console.log(oNew.oFavorite[1].reading);//picture
    console.log(oPerson.oFavorite[1].reading);//history book
    
    oNew.oAddress.province="shanghai";
    console.log(oPerson.oAddress.province);//beijing
    console.log(oNew.oAddress.province);//shanghai
    

    深度克隆的对象可以完全脱离原对象,我们对新对象的任何修给都不会反映到原对象中,这样深度克隆就实现了。
    PS:为什么deepClone这个函数中的result一定要判断类型?
    因为,如果你的result直接是{}对象,我明明传进去的是一个数组,结果你复制完了以后,变成了一个对象了。

    function deepClone(obj){
        var result={},oClass=isClass(obj);//不判断result类型
        for(key in obj){
            var copy=obj[key];
            if(isClass(copy)=="Object"){
                result[key]=arguments.callee(copy);
            }else if(isClass(copy)=="Array"){
                result[key]=arguments.callee(copy);
            }else{
                result[key]=obj[key];
            }
        }
        return result;
    }
    function isClass(o){
        if(o===null) return "Null";
        if(o===undefined) return "Undefined";
        return Object.prototype.toString.call(o).slice(8,-1);
    }
    //克隆一个数组
    var arr=["a","b","c"];
    var oNew=deepClone(arr);
    console.log(oNew);//Object {0: "a", 1: "b", 2: "c"} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值