理解js中的深拷贝与浅拷贝的基础是要知道js中基本数据类型和引用数据类型的不同的存储方式,这里不做过多介绍,有需要的请自行查阅资料学习.
1. 赋值
最简单的复制一个变量的方式就是赋值的方式.对于基本数据类型来说,赋值变量相当于将源变量的值赋值给目标变量,赋值后两者就没有关系了.
var a=3;
var b=a;
b=5;
console.log(a);// 3
但是,对于引用类型来说,赋值操作相当于把地址指针赋给新变量,他们指向的是同一个地址,若其中一个变了,则另一个也会跟着变.
var a=[1,2,3];
var b=a;
b.push(4);
console.log(a);// [1,2,3,4]
var obj1={
a:1,
b:[1,2,3]
}
var obj2=obj1;
obj1.a=3;
console.log(obj2.a);//3
obj2.b.push(5);
console.log(obj1.b);// [1,2,3,5]
2.浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。这样的拷贝方式就是浅拷贝.请看下面这个例子.
var obj1 = {
a:1,
b:[2,3]
};
var obj2 = obj1;
var obj3 = shallowCopy(obj1);
function shallowCopy(source) {
var target = {};
for (var prop in source) {
target[prop] = source[prop];
}
return target;
}
obj2.a = 5;
obj3.a = 6;
console.log(obj1.a,obj2.a,obj3.a);// 5 5 6
obj2.b.push(10);
obj3.b.push(11);
console.log(obj1.b,obj2.b,obj3.b);// 三个[2,3,10,11]
从上面的例子可以看到通过赋值操作得到的对象obj2,即使是基本数据类型的属性的值发生改变,原始对象的属性的值也会发生改变,因为obj2和obj1只是指针不同,但指向的实际上引用的是同一个对象.但obj3的a属性的值发生变化并没有影响到obj1的a属性的值,因为obj3是创建了一个新对象.
但是,为什么obj3的b属性的值变化就影响到了原始对象obj1呢,因为浅拷贝只复制一层对象的属性,并不包括对象里面的引用类型的数据.
除了上述手写函数实现对象浅拷贝的方式外,js还提供了Object.assign()方法实现浅拷贝.
var obj1={
a:1,
b:[2,3]
}
var obj2=Object.assign({},obj1);
3. 深拷贝
实际工作中,如果使用浅拷贝复制对象,会使得源对象和目标对象其中一个发生变化,另一个也跟着发生变化,导致项目出现"bug",所以我们经常想要的是拷贝一个对象后让源对象和目标对象再无关系,不再互相影响.那么深拷贝有哪些方式可以实现呢?
1)最简单的方式是将源对象转为json字符串再通过json.parse转换成对象.
var obj1={
a:1,
b:[2,3]
}
var obj2=JSON.parse(JSON.stringify(obj1));
obj2.b.push(4);
console.log(obj1.b);// [2,3]
2)递归拷贝
function deepCopy(sourceObj, targetObj) {
var resultObj = targetObj || {};
for (var i in sourceObj) {
var prop = sourceObj[i];
if(prop === resultObj) {
continue;
}
if (typeof prop === 'object') {
resultObj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, resultObj[i]);
} else {
resultObj[i] = prop;
}
}
return resultObj;
}
var obj1 = { a: {b: [2,3], c: 10} };
var obj2=deepCopy(obj1);
obj2.a.b.push(4);
console.log(obj1.a.b);// [2,3]
3)jquery 有提供一个$.extend可以实现深拷贝.
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
obj2.b.f.g=3;
console.log(obj1.b.f.g); // 1
console.log(obj1.b.f === obj2.b.f);// false
此外还有一些其它的第三方函数库可以实现深拷贝,如lodash中的lodash.cloneDeep()。