美文网首页
JavaScript关于对象的克隆

JavaScript关于对象的克隆

作者: 黄山淋Slahser | 来源:发表于2017-05-11 23:07 被阅读0次

    前言

    第一次写简书,简单写一些自己关于自己对前端的理解及实际工作中遇到的问题,如有错误,还请各位指正。

    我们都知道js里对象属于引用类型,仅仅是简单的赋值,只是将一个对象在堆中的地址赋给另一个变量,实际指向的还是同一内存空间:

    var obj1 = {a: 1,b: 2};
    obj2 = obj1;
    obj2  // {a: 1,b: 2}
    obj2.a = 2;
    obj1  // {a: 2,b: 2}
    

    实际上obj1和obj2指向的是相同的对象,所以对obj2进行操作会改变原对象,而在实际应用中,我们常常需要的是克隆一个相同的对象去进行操作。这时我们就会想到使用for(in)去遍历对象进行克隆:

    var obj1 = {a: 1,b: 2,c: 3};
    var obj2 = {};
    for(var key in obj1){
      obj2[key] = obj1[key]
    };
    obj2 // {a: 1,b: 2,c: 3}
    

    这个时候我们想到去写一个方法实现对象的克隆:

    function clone(obj){
        var result={};
        for(key in obj){
            result[key]=obj[key];
        }
        return result;
    }
    var obj1 = {
            x:1,
            y:2,
            z:{
                a:1,
                b:2,
                c:{
                    e:3,
                    f:4
                }
            }
        };
    var obj2 = clone(obj1)
       console.log(obj2.z.c.f); //4
       obj2.z.c.f = 10;
       console.log(obj1.z.c.f); //10
    

    可以看到我们的克隆并不彻底,对于对象中的对象并没有克隆出来,这时我们需要健壮程序,当我们传入的是一个对象,而如果对象的属性值不单单仅是对象呢?可能是数组,可能是null,或undefined,所以,我们需要对对象及他的属性进行类型检测:

    function clone(obj) {
            var result; // 
            if(obj === null){
                return 'null'  //如果是null,直接返回
            }
            else if(obj === undefined){
                return 'undefined'  //如果是undefined,直接返回
            }
            else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Object'){
                result = {}  //这种方式俗称为检查胎记,能直接返回对象的类属性
            }else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Array'){
                result = []
            }else{
                return  obj
            }
            for(var key in obj){
                if(Object.prototype.toString.call(obj[key]).slice(8,-1) ==='Object' || 'Array'){
                    result[key] = clone(obj[key]) //递归调用
                }else{
                    result[key] = obj[key];
                }
            }
            return result
        }
    var obj2 = clone(obj1)
       console.log(obj2.z.c.f); //4
       obj2.z.c.f = 10;
       console.log(obj1.z.c.f); //4
    

    这个时候,我们的方法基本完成,但是,我们有没有想过,如果for(in)循环拿不到对象的属性,那我们怎么办?等等,我们似乎忘记对象的属性还有四大特性了:

    var obj = {
            x: 1,
            y: 2
        };
        console.log(Object.getOwnPropertyDescriptor(obj,'x')); 
        // Object {value: 1, writable: true, enumerable: true, configurable: true}
        Object.defineProperty(obj,'x',{
            writable:false,
            enumerable:false,
        }); // 我们重新设置x的特性为不可读写,不可遍历
        obj.x = 2;
        console.log(obj.x);// 1
        var Oobj={};
        for(var key in obj){
            Oobj[key] = obj[key]
        }
        console.log(Oobj); //{y:2},我们压根就拿不到x,更不要说去克隆了
    

    这时我们就需要继续改进我们之前定义的克隆方法了

    function clone(obj) {
            var result; 
            if(obj === null){
                return 'null'  //如果是null,直接返回
            }
            else if(obj === undefined){
                return 'undefined'  //如果是undefined,直接返回
            }
            else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Object'){
                result = {}  //这种方式俗称为检查胎记,能直接返回对象的类属性
                var names = Object.getOwnPropertyNames(obj);// 拿到元素所有属性的键值
                for( var i=0 ;i< names.length; i ++){
                    var resc = Object.getOwnPropertyDescriptor(obj,names[i]); //拿到属性的特性
                    var copy = obj[names[i]]; //元素属性值
                    Object.defineProperty(result,names[i],resc);//重新添加属性并设置其特性
                    if(Object.prototype.toString.call(copy).slice(8,-1) ==='Object' || 'Array'){
                        result[names[i]] = clone(copy)
                    }
                }
            }else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Array'){
                result = [];
                for(var key in obj){
                    if(Object.prototype.toString.call(copy).slice(8,-1) ==='Object' || 'Array'){
                        result[key] = clone(obj[key]) //递归调用
                    }else{
                        result[key] = obj[key];
                    }
                }
            }else{
                return  obj
            }
            return result
        }
        var obj1 = {
            x:1,
            y:2,
            z:{
                a:1,
                b:[1,2,[3,4]],
                c:{
                    e:3,
                    f:4
                }
            }
        };
        Object.defineProperty(obj1.z,'c',{
            writable:false,
            enumerable:false,
        });
    obj2 = clone(obj1);
    console.log(obj2); // 与obj1一致 
    

    如有错误或遗漏,欢迎指正,谢谢。

    相关文章

      网友评论

          本文标题:JavaScript关于对象的克隆

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