js对象深拷贝/浅拷贝

125 阅读3分钟

前几天听项目组的同学调试bug,还像是对象的拷贝问题导致的,所以想整理一下,避免后面再踩坑。

js中数据的存储

先总结一下js中数据的类型和数据的存储方式;

数据类型

基本类型:undefined、null、boolean、number、string、Symbol
引用类型:Object

数据存储方式

js中数据有两种存储方式:栈存储和堆存储。其中基本数据类型主要放在栈中,引用类型主要放在堆中。 栈内存:基本类型在内存中占有固定大小的空间,他们的值保存在栈空间,我们通过按值来访问的。 堆内存:引用类型的地址是固定大小的,存储在栈内存中,引用类型的值大小是不固定的,所以,值是存在堆内存中的。

浅拷贝

浅拷贝MDN并没有给出明确的定义。 根据自己的理解,浅拷贝(浅复制)主要拷贝的数据类型是对象,浅拷贝只拷贝对象的第一层的属性,如果拷贝的对象有子对象,那么浅拷贝并不会递归的拷贝其子对象的属性。

var obj1={
    id:1,
    arr:[1,2]
}
function copy(obj){
    var o={}
    for (var prop in obj){
        if(obj.hasOwnProperty){
            o[prop]=obj[prop]
        }
    }
    return o;
}


obj2=copy(obj1);

obj2.id=2;
obj2.arr[0]=0

obj1.id//1
obj2.id//2

obj1.arr//[0,2]
obj2.arr//[0,2]

深拷贝

它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。

利用递归实现深拷贝(多层对象):

var obj1={
    id:1,
    arr:[1,2]
}
function copy(obj){
    var o={}
    for (var prop in obj){
        if(obj.hasOwnProperty){
            if(typeof(obj[prop])==='object'){
                o[prop]=obj[prop].constructor===Array?[]:{};
                o[prop]=copy(obj[prop])
            }else{
                o[prop]=obj[prop]
            }
            
        }
    }
    return o;
}


obj2=copy(obj1);

obj2.id=2;
obj2.arr[0]=0

obj1.id//1
obj2.id//2

obj1.arr//[1,2]
obj2.arr//[0,2]

JSON.parse(JSON.stringify(XXXX))实现对象的深拷贝(多层对象)

JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝。 参考javaScript中浅拷贝和深拷贝的实现

var obj1={
    id:1,
    arr:[1,2]
}
function copy(obj){
    var o={}
    o=JSON.parse(JSON.stringify(obj))
    return o;
}

obj2=copy(obj1);
obj2.id=2;
obj2.arr[0]=0

obj1.id//1
obj2.id//2

obj1.arr//[1,2]
obj2.arr//[0,2]

es6扩展运算符(...)实现对象的深拷贝(一层对象)

var obj1={
    id:1,
    name:'zhang'
}
obj2={...obj1};
obj2.id=2;


obj1.id//1
obj2.id//2

ES6的Object.assign(一层对象)

var obj1={
    id:1,
    arr:[1,2]
}
function copy(obj){
    var o={}
    o=Object.assign({},obj);
    return o;
}

obj2=copy(obj1);
obj2.id=2;
obj2.arr[0]=0

obj1.id//1
obj2.id//2

obj1.arr//[0,2]
obj2.arr//[0,2]

slice方法深拷贝数组

因为slice不是在原数组上修改,而是会返回新的数组。

var arr1=[1,2,3,4,5];
var arr2=arr1.slice(0);
arr2.push(6);
console.log(arr1);//[1, 2, 3, 4, 5]
console.log(arr2);//[1, 2, 3, 4, 5, 6]

concat方法,因为concat不是在原数组上修改,而是会返回新的数组

var arr1=[1,2,3,4,5];
var arr2=arr1.concat();
arr2.push(6);
console.log(arr1);//[1, 2, 3, 4, 5]
console.log(arr2);//[1, 2, 3, 4, 5, 6]

es6提供的方法 Array.from()

var arr1=[1,2,3,4,5];
var arr2=Array.from(arr1);
arr2.push(6);
console.log(arr1);//[1, 2, 3, 4, 5]
console.log(arr2);//[1, 2, 3, 4, 5, 6]

参考:javascript中的深拷贝和浅拷贝? javascript-详解javaScript的深拷贝 javaScript中浅拷贝和深拷贝的实现