前言:要理解JS对象的深浅拷贝的前提知识:需要了解JS的数据类型以及数据类型在内存中是如何存储的(栈跟堆)
1. 赋值 如图:将含有基础类型跟引用类型的obj赋值给obj1,并修改obj1里面的基础数据类型(属性name)以及引用数据类型(属性hobby),结果obj的属性值也跟着改变,这是属于赋值,不属于浅拷贝
2. 浅拷贝 浅拷贝与赋值最大的不同点在于浅拷贝会新创建一个对象,浅拷贝改变基础数据类型不会影响到原数据,但是改变引用数据类型就会影响到原数据(因为拷贝的是内存地址,还是指向同一个堆)
如下代码:定义一个浅拷贝函数,分别修改了obj1的基础数据类型属性name,obj的name不受影响,而改变了obj1的引用数据类型属性hobby,obj的hobby也跟着改变。
var obj = {name:'流川命',age:16,hobby:['篮球','睡觉']};
function shallowClone(obj = {}) {
if(typeof obj != 'object' || obj == null) {
return obj
}
let newObj;
if(obj instanceof Array) {
newObj = []
} else {
newObj = {}
}
for(let i in obj) {
if(obj.hasOwnProperty(i)) {
newObj[i] = obj[i]
}
}
return newObj;
}
let obj1 = shallowClone(obj);
obj1.name = '樱木花道'
obj1.hobby.push('吃饭')
console.log('obj===',obj)
console.log('obj1===',obj1)
结果如图:
3. 深拷贝 要将浅拷贝改成深拷贝,可以利用递归的方法,下面代码在for循环赋值的时候加了递归就可以实现(函数名改了一下,这个不重要),结果原数据的基本数据类型的属性name以及引用数据类型的属性hobby都不受影响。
var obj = {name:'流川命',age:16,hobby:['篮球','睡觉']};
function deepClone(obj = {}) {
if(typeof obj != 'object' || obj == null) {
return obj
}
let newObj;
if(obj instanceof Array) {
newObj = []
} else {
newObj = {}
}
for(let i in obj) {
if(obj.hasOwnProperty(i)) {
newObj[i] = deepClone(obj[i])
}
}
return newObj;
}
let obj1 = deepClone(obj);
obj1.name = '樱木花道'
obj1.hobby.push('吃饭')
console.log('obj===',obj)
console.log('obj1===',obj1)
结果如图:
4. 深拷贝的应用场景
- 在做增删改查中的修改功能时,利用深拷贝实现编辑页面正在编辑的内容与显示页面的数据互不影响。
注意:
- 可以巧妙的利用JSON.parse(JSON.stringify(obj))进行深拷贝,但是有弊端。原对象中如果含有Date对象,JSON.stringify()会将其变为字符串,之后并不会将其还原为日期对象。或是含有RegExp对象,JSON.stringify()会将其变为空对象,属性中含有NaN、Infinity和-Infinity,则序列化的结果会变成null,如果属性中有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失。
- 原生js方法slice、concat、filter、map都不是真正意义上的深拷贝,都仅只适用于一维数组。
|