浅谈浅拷贝与深拷贝

浅谈浅拷贝与深拷贝

我们知道对象是一种引用数据类型,当我们将一个对象赋值给另一个对象时,实际上是把对象的地址引用过去,当原对象的属性发生变化时,其他引用它的对象的属性也会发生变化;同样的当引用它的对象的属性发生变化时,原对象的属性也会发生变化。

let a = {
  age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
b.age = 3
console.log(b.age) // 3
console.log(a.age) // 3

但是实际使用过程中我们往往不希望这样,所以就要讨论到浅拷贝和深拷贝的问题:

1、浅拷贝

Object.assign

Object.assign 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。

let a = {
  age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

···运算符

另外我们还可以通过展开运算符 ... 来实现浅拷贝

let a = {
  age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1

2、深拷贝

通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就可能需要使用到深拷贝了

let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native

JSON.parse(JSON.stringify(object))

当拷贝的对象属性中是一些普通对象时可以使用JSON.parse(JSON.stringify(object))

let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

但是这种方法也有局限性:

  • 会忽略掉undefined
  • 会忽略symbol
  • 不能序列化函数
  • 不能解决循环引用的对象
let a = {
  age: undefined,
  sex: Symbol('male'),
  jobs: function() {},
  name: 'cxk'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "cxk"}

MessageChannel

如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 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: 2
  },
  d: undefined
}

obj.b.d = obj.b

// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
const test = async () => {
  const clone = await structuralClone(obj)
  console.log(clone)
}
test()

该方法可以处理循环引用对象和undefined,但是依然不能处理函数

简易版深拷贝

function deepClone(obj) {
  function isObject(o) {
    return (typeof o === 'object' || typeof o === 'function') && o !== null
  }

  if (!isObject(obj)) {
    throw new Error('非对象')
  }

  let isArray = Array.isArray(obj)
  let newObj = isArray ? [...obj] : { ...obj }
  Reflect.ownKeys(newObj).forEach(key => {
    newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
  })

  return newObj
}

let obj = {
  a: [1, 2, 3],
  b: {
    c: 2,
    d: 3
  },
  e: undefined,
  f: function aa(){},
  g: Symbol('bb')
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2

该方法仅仅是深拷贝的简单实现方法,依然不能考虑到很多边界情况,但是这已经能满足绝大多数使用情况,想要了解更深入的深拷贝,建议百度lodash的深拷贝函数。

posted @ 2021-11-01 21:04  东方初白  阅读(13)  评论(0)    收藏  举报