深浅拷贝-对象
-
深拷贝:将整个对象都拷贝过来,嵌套的复杂数据类型和原对象这个属性指向不同的地址。
-
浅拷贝:只拷贝对象的第一层,嵌套的复杂数据类型依旧和原对象这个属性指向相同的地址。
1. JSON.Stringify
- 这个可以实现深拷贝,浅拷贝,但是有问题,某些值不能序列化,会丢失。
1-1. 对象只有一层
只有一层的对象,使用JSON.stringify序列化的对象,修改值不会相互影响,实现了浅拷贝。
let obj = {
uname:'zrs',
age:12
}
let newObj=JSON.parse(JSON.stringify(obj))
console.log("obj",obj);
console.log("newObj",newObj);
console.log("修改之后---------------------------");
obj.uname="zzzz"
console.log("obj",obj);
console.log("newObj",newObj);
1-2. 多层对象
多层对象嵌套的时候,使用JSON.stringify序列化的对象,修改值不会相互影响,实现了深拷贝
let obj2 = {
uname:'zrs',
age:12,
son:{
uname:'erha'
}
}
let newObj2=JSON.parse(JSON.stringify(obj2))
console.log("obj",obj2);
console.log("newObj",newObj2);
console.log("修改之后-----------------------");
obj2.uname="zzzz"
obj2.son.uname="qqqqqqqqqqqqq"
console.log("obj",obj2);
console.log("newObj",newObj2);
1-3. 会被忽略的值
根据结果可以得出:使用JSON.stringify序列化的对象,如果包含属性值为 undefined 或者 function 或者 Symbol 类型的属性,那么会在序列化的时候,被忽略。
let obj3= {
uname:null,
age:undefined,
son:{
uname:'erha'
},
func:function(a){
console.log(a);
},
sym:Symbol('zrs'),
}
let newObj3=JSON.parse(JSON.stringify(obj3))
console.log("obj",obj3);
console.log("newObj",newObj3);
1-4. 会被转化为 null 的值
从结果得出:如果对象的属性的属性值包含 NaN 、Infinity 、-Infinity ,那么在序列化的时候,会被转换成为 null。
let obj4= {
uname:null,
son:{
uname:'erha'
},
num1:NaN,
num2:Infinity,
num3:-Infinity,
}
let newObj4=JSON.parse(JSON.stringify(obj4))
console.log("obj",obj4);
console.log("newObj",newObj4);
2. 展开运算符
- 展开运算符,可以实现对象的浅拷贝,没有
JSON.stringify的限制。 - 不能靠自己实现深拷贝。
// 展开运算符
let obj5={
uname:'zrs',
age:16,
son:{
uname:'zrr',
age:2
},
f:function(a){
console.log(a);
},
money:null,
wife:undefined,
house:NaN,
dream:Infinity,
blank_dream:-Infinity,
}
let newObj5={...obj5}
console.log("obj5",obj5);
console.log("newObj5",newObj5);
// 以上两个输出结果可以看出,展开运算符不受限制,没有JSON.stringify 的限制。
console.log("修改之后------------------");
obj5.son.uname="小小张"
console.log("obj5",obj5);
console.log("newObj5",newObj5);
// 以上代码的输出结果,可以得知,展开运算符只能事项浅拷贝,即拷贝对象的第一层
3. 你自己写递归实现
let obj6={
uname:'zrs',
age:16,
son:{
uname:'zrr',
age:2
},
f:function(a){
console.log(a);
},
money:null,
wife:undefined,
house:NaN,
dream:Infinity,
blank_dream:-Infinity,
}
// 实现深拷贝的函数
const cloneData=data=>{
// 根据传入的数据类型,创建新的数据是简单对象还是数组
const newData=Array.isArray(data)?[]:{}
// 遍历传入的数据
for (let key in data) {
// 属性存在,且为对象,那么直接递归一下
if(data[key] && typeof data[key]==='object'){
newData[key]=cloneData(data[key]) // 递归
}else{
newData[key]=data[key]
}
}
return newData
}
let newObj = cloneData(obj6)
console.log("obj6",obj6);
console.log("newData",newObj);
newObj.uname="xxxxxx"
newObj.son.uname="小xxxxxx"
console.log("修改之后------------------------");
console.log("obj6",obj6);
console.log("newData",newObj);
// 以上两行代码的输出结果可以得出,修改新对象的值之后,老对象的值未发生变化,所以实现了深拷贝。
4. Object.asign 实现对象的浅拷贝
浅拷贝
// 当 Object.assign 只有一个参数的时候,该方法会返回一个对象,这个对象是对参数的浅拷贝。
let newObj = Object.assign(obj7)
console.log("obj7", obj7);
console.log("newObj", newObj);
newObj.uname = 'xxxx'
newObj.son.uname = '小小小xxxx'
console.log("obj7", obj7);
console.log("newObj", newObj);
如果 Object.assign 的第一个参数是基础数据类型,那么会被包装成为一个对象,然后返回。
let newObj2 = Object.assign(1)
console.log(newObj2);
合并对象
Object.assign("目标对象" , "源对象1","源对象2",...)
如果 Object.assign 有多个参数,那么源对象的属性会被合并到目标对象上,如果存在相同的属性,则最后的源对象的属性会覆盖之前属性的属性值。Object.assign 对对象的合并实现的是浅拷贝。
let obj1={
uname:'zrs',
age:12,
son:{
uname:'zrr'
}
}
let obj2={
money:null,
wife:undefined
}
let obj3={
money:100,
wife:undefined,
}
// 合并之后的新对象
let newObj=Object.assign(obj1,obj2,obj3)
console.log(newObj);