浅拷贝、深拷贝

230 阅读1分钟

对象赋值的时候,其实是拷贝了对象的地址。所以改变一方,其他地方也都会改变。

let p1 = {
    age: 18
};
let p2 = p1;
p2.age = 20;
console.log(p1.age); // 20

浅拷贝

Object.assign和展开运算符...可以实现浅拷贝。

浅拷贝会拷贝对象中的所有属性到新对象中,如果属性值是对象,则拷贝的是对象的地址。所以还是存在修改了一处,其他地方也会改变的问题。

let obj = {
    a: 1,
    b: [1,2,3]
};
let newObj = Object.assign({},obj);
// let newObj = {...obj};
obj.a = 2;
console.log(newObj.a); // 1 属性值为非对象时,浅拷贝可以解决问题
obj.b.pop();
console.log(newObj.b);// [1,2] 属性值为对象时,浅拷贝只是拷贝了对象的地址,还是存在问题

深拷贝

彻底解决拷贝对象会共享数据的问题。

  1. 使用JSON.parse(JSON.stringify(obj))实现深拷贝
let obj = {
    a: 1,
    b: [1,2,3]
};
let newObj = JSON.parse(JSON.stringify(obj));

上述方法会忽略掉undefined和函数。且不能深拷贝存在循环引用的对象。

let obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  },
}
obj.c = obj.b
obj.b.c = obj.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)

  1. 不包含函数的情况下,可以使用MessageChannel
let obj = {
    a: 1,
    b: {
        c: 2,
        d: 3,
    },
    e: undefined
};
obj.c = obj.b;
obj.b.c = obj.c;
function deepClone(obj) {
    return new Promise(resolve => {
        const { port1, port2 } = new MessageChannel();
        port2.onmessage = res => resolve(res.data);
        port1.postMessage(obj);
    });
}
let newObj;
let setNewObj = async () => {
    newObj = await deepClone(obj);
    console.log(newObj);
}
setNewObj();
  1. 自己实现简单的深拷贝,可以使用loadsh的深拷贝函数
function deepClone(obj) {
    if(!isObject(obj)){
        throw new Error("非对象");
    }
    function isObject(o) {
        return typeof o === 'object' && o !== null;
    }
    let newObj = Array.isArray(obj) ? [] : {};
    Object.keys(obj).forEach(key => {
        newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key];
    });
    return newObj;
}
let obj = {
    a: 1,
    b: [1,2,3]
};
let newObj = deepClone(obj);