深浅拷贝-对象

126 阅读3分钟

深浅拷贝-对象

  • 深拷贝:将整个对象都拷贝过来,嵌套的复杂数据类型和原对象这个属性指向不同的地址。

  • 浅拷贝:只拷贝对象的第一层,嵌套的复杂数据类型依旧和原对象这个属性指向相同的地址。

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 的值

从结果得出:如果对象的属性的属性值包含 NaNInfinity-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);