浅复制是指当对象的字段值被复制时,字段引用的对象不会被复制·例如,如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅复制,那麽两个对象将引用同一个字符串·
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人。
举几个简单的例子来看一下深浅拷贝:
// 浅拷贝:
let arr1 = [1, 2, 3, 4];
let arr2 = arr1;
console.log('arr2打印前:', arr2);
arr2.push(5);
console.log("arr2打印后:", arr2);
console.log("arr1打印后:", arr1);
// 深拷贝
let arr3 = [1, 2, 3, 4];
let arr4 = [];
let i;
for (i = 0; i < arr3.length; i++) {
arr4.push(arr3[i]);
}
console.log('arr4打印前:', arr4);
arr4.push(5);
console.log("arr4打印后:", arr4);
console.log("arr3打印后:", arr3);
简单来说,
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。
假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝(修改堆内存中的不同的值)
浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
深复制:是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,在计算机中开辟一块新的内存地址用于存放复制的对象。
使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。
浅拷贝实例:
//此递归方法不包含数组对象
var obj = {
a: 1,
arr: [2, 3]
};
var shallowObj = shallowCopy(obj);
console.log(shallowObj);
function shallowCopy(src) {
var newobj = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
newobj[prop] = src[prop];
}
}
return newobj;
}
因为浅复制只会将对象的各个属性进行复制,并不会进行递归复制,而JavaScript存储对象是存地址的,所以浅复制会导致Obj.arr和shallowObj.arr指向同一块内存地址.
// 导致的结果就是:
shallowObj.arr[1] = 5;
console.log(obj.arr[1]); //5
深拷贝实例:
var obj1 = {
a: 1,
arr: [1, 2, 3, 4, 5],
nation: '中国',
birthplaces: ['成都', '绵阳', '重庆']
};
var obj2 = {
name: '吴'
};
obj2 = deepCopy(obj1, obj2);
console.log(obj2);
//深复制,要想达到深复制就需要用递归
function deepCopy(o, c) {
var c = c || {};
for (var i in o) {
if (typeof o[i] === 'object') {
if (o[i].constructor === Array) {
//这是数组
c[i] = [];
} else {
//这是对象
c[i] = {};
}
deepCopy(o[i], c[i]);
} else {
c[i] = o[i];
}
}
return c;
}
而深复制则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在obj和shallowObj的arr属性指向同一个对象的问题。