工作所需,我这个后端程序猿又开始操起我那半吊子的前端技术,开始写前端。这次在实现前端页面逻辑的时候,碰到了一个JS深拷贝的问题。原先默认都是浅拷贝,或者是第一层深拷贝,但是到里面的嵌套对象就是浅拷贝了。踩了这个坑之后,遂记录本文以供后续参考。
本文重点介绍JS中如何实现深拷贝,避免在实际开发中踩坑。当然,考虑到部分同学对深拷贝和浅拷贝的概念可能不太熟,我们也会先介绍一下深拷贝和浅拷贝的原理。
深拷贝 VS 浅拷贝
数据类型
首先我们了解下两种数据类型:
- 基本类型:像Number、String、Boolean等这种为基本类型
- 复杂类型:Object和Array
浅拷贝与深拷贝的概念
接着我们分别来了解下浅拷贝和深拷贝,深拷贝和浅拷贝是只针对Object和Array这样的复杂类型的。
浅拷贝
1 | var a = { |
1 | var a = ['myname', 'yana']; |
可以看出,对于对象或数组类型,当我们将a赋值给b,然后更改b中的属性,a也会随着变化。
也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝。
JavaScript 并没有直接提供对象或者数组的复制方法,默认采用浅拷贝。
深拷贝
刚刚我们了解了什么是浅拷贝,那么相应的,如果给b放到新的内存中,将a的各个属性都复制到新内存里,就是深拷贝。也就是说,当b中的属性有变化的时候,a内的属性不会发生变化。
如何实现深拷贝
对象的深拷贝
通过序列化实现深拷贝
我们可以将对象序列化再解析回来实现对象拷贝(注意:对象中的函数 function 不会被复制)1
2
3
4
5var a = {v1:1, v2:2};
var b = JSON.parse(JSON.stringify(a));
b.v1 = 3;
console.log("对象a:",a);
console.log("对象b:",b);
通过递归遍历实现
我们定义一个 clone 方法实现深度复制功能(Deep Copy),其内部实现原理就是将对象的属性遍历一遍,赋给一个新的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//自定义的复制方法
function clone(obj) {
var copy = {};
for (var attr in obj) {
copy[attr] = typeof(obj[attr])==='object' ? clone(obj[attr]) : obj[attr];
}
return copy;
}
//测试样例
var a = {v1:1, v2:2};
var b = clone(a);
b.v1 = 3;
console.log("对象a:",a);
console.log("对象b:",b);也可以直接给 Object 增加个 clone 方法,其内部实现原来同上面是一样的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//自定义的复制方法
Object.prototype.clone = function() {
var copy = (this instanceof Array) ? [] : {};
for (var attr in this) {
if (this.hasOwnProperty(attr)){
copy[attr] = typeof(this[attr])==='object' ? clone(this[attr]) : this[attr];
}
}
return copy;
};
//测试样例
var a = {v1:1, v2:2};
var b = a.clone();
b.v1 = 3;
console.log("对象a:",a);
console.log("对象b:",b);
使用 jQuery 复制
jQuery 自带的 extend 方法可以用来实现对象的复制。1
2
3
4
5
6var a = {v1:1, v2:2};
var b = {};
$.extend(b,a);
b.v1 = 3;
console.log("对象a:",a);
console.log("对象b:",b);
数组的拷贝
使用 slice 方法实现
1 | var a = [1, 2]; |
使用 concat 方法实现
1 | var a = [1, 2]; |