前言
本文介绍js中深拷贝和浅拷贝的几种方法,以及深拷贝和浅拷贝的区别
深拷贝和浅拷贝的区别
- 深拷贝(复杂):复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制;
- 浅拷贝:复制一份引用,所有引用对象都指向一份数据,并且都可以修改这份数据
深拷贝和浅拷贝只针对像 Object, Array 这样的复杂对象。它们最根本的区别在于是否真正获取了一个对象的复制实体,而不是引用。简单来说,假设B复制了A,当修改B时,看A是否会发生变化,如果A变了,说明是浅拷贝;如果A没变,那就是深拷贝
浅拷贝
1.直接赋值
代码如下(示例1):
var obj = {
name:123,
list:[1,2,3,4,5],
fn(){
alert(this.name)
},
info:{
title:'标题',
subtitle:'副标题'
}
};
var obj2 = obj;
obj2.name = 456;
obj2.list.push(6,7,8);
console.log(obj2.name,obj.name) //456,456
console.log(obj2.list,obj.list) //[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7,8]
示例1中将obj直接赋值给obj2,当修改obj2里任意属性时,obj也会被修改
2.for…in
代码如下(示例2):
var obj = {
name:123,
list:[1,2,3,4,5],
fn(){
alert(this.name)
},
info:{
title:'标题',
subtitle:'副标题'
}
};
var obj2 = {};
for(let x in obj){
obj2[x]=obj[x]
};
obj2.name = 456;
obj2.list.push(6,7,8);
obj2.info.title = '我是obj2的标题';
console.log(obj2.name,obj.name) //456,123
console.log(obj2.list,obj.list) //[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7,8]
console.log(obj2.info.title,obj.info.title)//我是obj2的标题,我是obj2的标题
示例2通过for…in循环,将obj里的每一项数据都赋值到obj2里。当修改obj2.name时,obj.name不会发生变化,但是修改obj2.list和obj2.info.title时,obj.info.title和obj.list也会发生变化
3.Object.assign
代码如下(示例3):
var obj = {
name:123,
list:[1,2,3,4,5],
fn(){
alert(this.name)
},
info:{
title:'标题',
subtitle:'副标题'
}
};
var obj2 = Object.assign({},obj);
obj2.name = 456;
obj2.list.push(6,7,8);
obj2.info.title = '我是obj2的标题';
console.log(obj2.name,obj.name) //456,123
console.log(obj2.list,obj.list) //[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7,8]
console.log(obj2.info.title,obj.info.title)//我是obj2的标题,我是obj2的标题
示例3,结果同for…in循环
总结
当拷贝的属性不为array和object时,使用for…in和Object.assign可以实现深拷贝,否则,array和object为浅拷贝
深拷贝
1.JSON.parse(JSON.stringify())
代码如下(示例4):
var obj = {
name:123,
list:[1,2,3,4,5],
tel:/^1[345789]\d{9}$/,
time:new Date(),
fn(){
alert(this.name)
},
info:{
title:'标题',
subtitle:'副标题'
}
};
var obj2 = JSON.parse(JSON.stringify(obj))
obj2.name = 456;
obj2.list.push(6,7,8);
obj2.info.title = '我是obj2的标题';
console.log(obj2.name,obj.name) //456,123
console.log(obj2.list,obj.list) //[1,2,3,4,5,6,7,8],[1,2,3,4,5]
console.log(obj2.info.title,obj.info.title)//我是obj2的标题,标题
console.log(obj2.tel,obj.tel)//{}, /^1[345789]\d{9}$/
console.log(obj2.time,obj.time)//2021-03-22T07:58:26.788Z,Mon Mar 22 2021 15:58:26 GMT+0800 (中国标准时间)
obj2.fn() //Uncaught TypeError: obj2.fn is not a function
示例4,看似实现了对obj对象的深拷贝,但这样导致了fn事件的丢失、正则失效、时间格式发生变化
2.递归
递归所有层级属性
代码如下(示例5):
function deepCopy(obj) {
if (typeof obj === 'object') {
var result = obj.constructor === Array ? [] : {};
for (var i in obj) {
result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
}
} else {
var result = obj;
}
return result;
}
var obj = {
name:123,
list:[1,2,3,4,5],
fn(){
alert(this.name)
},
info:{
title:'标题',
subtitle:'副标题'
}
};
var obj2 = deepCopy(obj)
obj2.name = 456;
obj2.list.push(6,7,8);
obj2.info.title = '我是obj2的标题';
console.log(obj2.name,obj.name) //456,123
console.log(obj2.list,obj.list) //[1,2,3,4,5,6,7,8],[1,2,3,4,5]
console.log(obj2.info.title,obj.info.title) //我是obj2的标题 标题
obj2.fn(); //alert(456)
案例5,不仅实现了对array和object对象的深拷贝,还实现了对function的拷贝。
总结
如果对象中没有函数、正则、date,可以使用JSON.parse(JSON.stringify())来实现深拷贝,否则,需要使用递归来循环所有层级的属性