深浅拷贝

2020年2月13日
let a = {
    name:"王芳"
}
let b = a
a.name = "刘毅"
console.log(b.name) // 刘毅

如果给一个变量赋值一个对象,那么这俩者的值都会是同一个引用,其中一方改变,另一方也将改变.

可以使用浅拷贝来解决这个问题

浅拷贝

  1. 通过 Object.assign 来解决这个问题
    let a = {
        name:"王芳"
    }
    let b = Object.assign({}, a)
    a.name = "刘毅"
    console.log(b.name) // 王芳
  2. 通过运算符 ... 来解决这个问题
    let a = {
        name:"王芳"
    }
    let b = {...a}
    a.name = "刘毅"
    console.log(b.name) // 王芳

如果当对象里的值还有对象时,浅拷贝就无法解决这种情况,这时要引入深拷贝

   let a = {
       name:"王芳",
       birthday:{
           year:1997,
           month:12,
           day:8
       }
   }
let b = Object.assign({}, a)
let c = {...a}
a.birthday.month = 10
console.log(b.birthday.month) // 10
console.log(c.birthday.month) // 10

深拷贝

通过 JSON.parse(JSON.stringify(object)) 来解决。

let a = {
        name:"王芳",
        birthday:{
            year:1997,
            month:12,
            day:8
        }
    }
let b = JSON.parse(JSON.stringify(a))
let c = JSON.parse(JSON.stringify(a))
a.birthday.month = 10
console.log(b.birthday.month) // 12
console.log(c.birthday.month) // 12

局限性

  1. 会忽略 undefined
  2. 会忽略 symbol
  3. 不能序列化函数
let a = {
    age: undefined,
    sex: Symbol('male'),
    jobs: function() {},
    name: '王芳'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "王芳"}
  1. 不能解决循环引用的对象
let obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  },
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)

你会发现在上述情况中,该方法会忽略掉函数和 undefined

但是在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问题,并且该函数是内置函数中处理深拷贝性能最快的。当然如果你的数据中含有以上三种情况下,可以使用 lodash 的深拷贝函数。

如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel

function structuralClone(obj) {
  return new Promise(resolve => {
    const {port1, port2} = new MessageChannel();
    port2.onmessage = ev => resolve(ev.data);
    port1.postMessage(obj);
  });
}

var obj = {a: 1, b: {
    c: b
}}
// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
(async () => {
  const clone = await structuralClone(obj)
})()