js对象深拷贝和浅拷贝

数据类型

javascript中对象为引用类型,其值存储在堆内存中, 深拷贝就是将重新开辟一块新的堆内存来赋值

浅拷贝

1
2
3
4
var obj={name:'张三'};
var newObj=obj
newObj.name='李四'
console.log(obj)//李四 newObj改变,obj也跟着改变 。因为二者指向同一个地址

深拷贝

1
2
3
4
 var obj={name:'张三'};
var newObj={};
newObj.name='李四';
console.log(obj)//张三 newObj改变,obj不变。因为newObj重新创建了一块堆内存,跟obj互不影响。这就是深拷贝

当然这只是最简单的深拷贝,如果obj属性的值又是一个对象呢。
比如:

1
2
3
4
5
var obj={name:{chinaName:'张三',EnglishName:'zhangsan'}}
var newObj={};
newObj.name=obj.name
newObj.name.chinaName='李四';
console.log(obj)//{name: {chinaName: "李四", EnglishName: "zhangsan"}}

这个时候obj又受到影响。因为obj的属性又是一个对象。我们只是外层创建了新的对象newobj={};但是里面的name对象还是直接赋值newObj.name=obj.name
因此我们需要再次创建一个新对象(开辟一块新的堆内存),然后把name这个对象里的赋值进去不就行了吗?进行改进一下

1
2
3
4
5
6
var obj={name:{chinaName:'张三',EnglishName:'zhangsan'}}
var newObj={};
newObj.name={}
newObj.name.chinaName='张三'
newObj.name.EnglishName='zhangsan'
console.log(newObj)//{name: {chinaName: "张三", EnglishName: "zhangsan"}}

改变一下name对象属性

1
2
newObj.name.chinaName='李四'
console.log(obj)//{name: {chinaName: "张三", EnglishName: "zhangsan"}} 好了,obj不受影响了。

但是如果里面有很多层属性,并且属性的值都是对象呢?我们就这样写下去肯定不行啊
于是我们想起来,递归函数 只要调用自身就可以处理一些相同的逻辑.

那实现一个深拷贝也就不难了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deepClone (obj) {
if(typeof(obj) !== 'object'){
return
}
var newObj = obj instanceof Array ? [] :{}
for(var i in obj){
if(obj[i] && typeof(obj[i]) === 'object'){
newObj[i] = deepClone(obj[i])
}else{
newObj[i] = obj[i]
}

}
return newObj;
}

验证一下

1
2
3
4
var a=deepClone(obj)
console.log(a,111)
a.name.EnglishName='lisi'
console.log(obj)//{name: {chinaName: "张三", EnglishName: "zhangsan"}} ,不受影响,没毛病

还有一种更简便的办法,使用json.stringify(),转为字符串类型,然后再json.parse转为对象

1
2
3
4
5
6
7
function deepCLone1 (obj){
return JSON.parse(JSON.stringify(obj))
}
var a1=deepClone(obj)
console.log(a1,111)
a1.name.EnglishName = 'lisi'
console.log(obj)//{name: {chinaName: "张三", EnglishName: "zhangsan"}}