JavaScript关于对象的克隆

2017-05-11  本文已影响0人  黄山淋Slahser

前言

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

我们都知道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一致 

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

上一篇下一篇

猜你喜欢

热点阅读