javascript 数组、对象深度克隆

最近项目过程中针对数组及对象的赋值发现以下问题

情况一:

 var A={age:12,name:'anne'},B=A;
    B.grade='五年级';
    console.log('A:',A,'B:',B);  
    //A:{age:12,name:'anne',grade:'五年级'} B: {age:12,name:'anne',grade:'五年级'}

情况二:

    var A={age:12,name:'anne'},
    B={}; //初始化对象B
    B=A;
    B.grade='五年级';
    console.log('A:',A,'B:',B);  
    //A:{age:12,name:'anne',grade:'五年级'} B: {age:12,name:'anne',grade:'五年级'}
 

以上两种我们会发现原本的目的是想改变对象B,但实际情况是对象A也跟着被改变,且初始化对象B也是无效;

javascript中有两种类型,一种叫做基本数据类型,一种就是引用类型

基本数据类型有 String Number Boolean undefined null

引用数据类型有 Object Array 等 (Function Regexp)

因为对象是引用类型,用对象存储的是对象的引用地址,而把对象的实际内容单独存放,所以对象的赋值是默认引用对象地址,修改的时候改变的是同一个地址的对象,所有互相干扰,如果你想要复制赋值,则必须要重新分配对象

我们给出以下几种写法:

  1. 使用Object.assign()
    该方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
 var A={age:12,name:'anne'}, B=Object.assign({},A); //将一个空对象和A复制到同一个目标对象
    
    B.grade='五年级';
    
    console.log( 'A:', A, 'B:' ,B );  
    //A:{age:12,name:'anne'} B: {age:12,name:'anne',grade:'五年级'}
  1. JSON对象序列化方法
    这个方法明显是简单得多,但是有个弊端,就是不能复制函数
var obj = {a:1,b:2}  
var newObj = JSON.parse(JSON.stringify(obj));   
newObj.c=3;
console.log(obj)  //{a:1,b:2} 
console.log(newObj)  //{a:1,b:2,c:3}
  1. 封装的JSON对象序列化(兼容处理,不能复制函数)
var cloneObj = function(obj){
      var str, newobj = obj.constructor === Array ? [] : {};
    if(typeof obj !== 'object'){
         return;
      } else if(window.JSON){
          str = JSON.stringify(obj), //序列化对象
          newobj = JSON.parse(str); //还原
     } else {
         for(var i in obj){
            newobj[i] = typeof obj[i] === 'object' ? cloneObj(obj[i]) : obj[i]; 
         }
     }
     return newobj;
};
  1. 完整的深度克隆对象
function deepClone(obj){
    var newObj,oClass=isClass(obj);
         //确定newObj的类型
    if(oClass==="Object"){
        newObj={};
    }else if(oClass==="Array"){
        newObj=[];
    }else{
        return obj;
    }
    for(key in obj){
        var _key=obj[key];
        if(isClass(_key)=="Object"){
            newObj[key]=
            (_key);//递归调用
        }else if(isClass(_key)=="Array"){
            newObj[key]=arguments.callee(_key);
        }else{
            newObj[key]=obj[key];
        }
    }
    return newObj;
}
function isClass(obj){
    if(obj===null) return "Null";
    if(obj===undefined) return "Undefined";
    return Object.prototype.toString.call(obj).slice(8,-1);
}


/*直接测试带函数的对象克隆*/
var obj = {a:1,b:2,c:()=>{console.log("this's a function")}} ;
var newObj=deepClone(obj);
newObj.d=function(){
	console.log('The second function ');
}
newObj.d;     // f(){console.log('The second function ');}
obj.d             // undefined

/*数组克隆*/
var arr = ['a','b'];
var newArr=deepClone(arr);
newArr[2]='c';
console.log('arr=>',arr,'newArr=>',newArr)     //arr=> ['a','b'] newArr=> ['a','b','c']

从上面的代码可以看到,深度克隆的对象可以完全脱离原对象,我们对新对象的任何修改都不会反映到原对象中,这样深度克隆就实现了。

这里要注意一点的就是:deepClone这个函数中的newObj一定要判断类型?newObj直接是对象,后面会导致明明传进去的是一个数组,结果复制完了以后,变成了一个对象了。
  如下例:

function deepClone(obj){
    var newObj={},oClass=isClass(obj);
    for(key in obj){
        var _key=obj[key];
        if(isClass(_key)=="Object"){
            newObj[key]=arguments.callee(_key);//递归调用
        }else if(isClass(_key)=="Array"){
            newObj[key]=arguments.callee(_key);
        }else{
            newObj[key]=obj[key];
        }
    }
    return newObj;
}
function isClass(obj){
    if(obj===null) return "Null";
    if(obj===undefined) return "Undefined";
    return Object.prototype.toString.call(obj).slice(8,-1);
}

var arr = ['a','b'];
var newArr=deepClone(arr); 
console.log(newArr) //{0:'a',1:'b'}

猜你喜欢

转载自blog.csdn.net/qq_41194534/article/details/84988897