javascript深拷贝和浅拷贝的思考

在做一个数据对象的时候需要拷贝一下对象,突然想到以前在js中存在的深拷贝和浅拷贝,这里复习一下也想深究一下。
首先看一段代码

1
2
3
var obj = { a : 1, arr: [1,2]}
var obj1 = obj; //浅拷贝
var obj2 = deepCopy(obj); //深拷贝

js是把对象数组存储在某一块内存区域的,所以浅拷贝会将objobj1指向同一块内存地址。而深拷贝一般都是开辟一块新的内存地址,将原对象的各个属性逐个复制出去。简单图示如下(图转自知乎):

那么就很容易理解如下代码了

1
2
3
obj.a = 10;
console.log(obj1.a); //输出10
console.log(obj2.a); //输出1

介绍一些简单的解决深拷贝的方法。

1.单纯的数组可以使用slice()方法进行拷贝

1
2
3
4
5
6
7
var arr = [1,2,3,4,5];
var arr2 = arr;
var arr3 = arr.slice(0);
arr[4] = 5656565;
console.log(arr); //[1,2,3,4,5656565]
console.log(arr2); //[1,2,3,4,5656565]
console.log(arr3); //[1,2,3,4,5]

2.借助JSON全局对象

针对纯的JSON对象拷贝可以使用JSON.stringifyJSON.parse方法,原理也很简单,将对象转换为JSON字符串在重新序列化一下。这是一个简单讨巧的方法。但是这个方法只能针对诸如Number, String, Boolean, Array, 扁平对象等,对如DateRegExp这类对象并不适用。

1
2
3
function jsonClone(obj) {
return JSON.parse(JSON.stringify(obj));
}

3.利用jQuery的$.extend()方法

jQuery的$.extend()可以将多个对象合并并返回合并后的对象,我们可以将需要拷贝的数据和{}进行合并。值得注意的是当出现嵌套对象的情况下,$.extend()提供了第一个参数来进行递归的深拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var people = {
name : 'perez',
age : 26,
friends : {
people : {
name : 'butsalt'
}
}
}

var people2 = $.extend({},people);
var people3 = $.extend(true,{},people);

people.friends.people.name = 'butsaltme';
console.log(people2.friends.people.name); //butsaltme

console.log(people.friends.people === people2.friends.people); //true
console.log(people.friends.people === people3.friends.people); //false

网上有段如下代码,能解决大多数情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
};

参考资料:深入剖析 JavaScript 的深复制