浅谈JavaScript深拷贝

前言

JavaScript的浅拷贝、深拷贝是一个老生常谈的话题,真正完美的深拷贝其实是比较困难的,但相对的能应用的场景也同样比较少,个人感觉浅拷贝和深拷贝的核心概念无非是对JavaScript引用类型的理解,普通的值类型可以直接复制,应用类型的直接复制则是对其地址的复制,这个其实就是C语言中的指针的概念。明白了这些深浅拷贝就不难理解了,接下来分享一些常用的操作。

浅拷贝

浅拷贝就是不考虑引用类型,我们把对象的属性或是数组的元素都当作原始类型来看待。

Object.assign() 
这个方法是用来合并两个对象的属性,将要拷贝的对象和一个空对象合并就能成为一个新的对象

cosnt temp = { a: 1, b: 2}
cosnt obj = Object.assign({}, temp)

temp === obj // false
es6扩展运算符 ...

cosnt temp = { a: 1, b: 2}
cosnt obj = { ...temp }
这种方法可以将temp里的属性提取出来放到新的对象里面

const array = [1,2,3,4,5]
cosnt newArray = [...array]
数组也是同理

深拷贝

深拷贝要比浅拷贝难实现的多,浅拷贝的方式会把引用类型的地址直接复制过去,不同的两个对象里面的引用类型的属性会互相影响,这种有时候不会符合我们的要求,此时就需要深拷贝。

JSON.parse(JSON.stringify())

这种配合使用算是比较常用的一种深拷贝方式了,将对象转成字符串,再重新生成对象,这样新生成的对象的引用类型和原对象里面的就不同了。

cosnt temp = { a: 1, b: 2, c: { d: 1 } }
cosnt obj = JSON.parse(JSON.stringif(temp))

temp === obj // false
temp.c === obj.c // false

这种方式的缺陷是undefined, 函数和对象内部循环引用的属性无法拷贝

MessageChannel

MessageChannel的介绍可以查看MDN,这个API其实是建立一个消息管道来进行页面通信使用的,使用这个API的比JSON的方法的优势在于可以进行有循环引用对象的拷贝,但是依然无法拷贝函数

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

const temp = { a: 1, b: 2, c: { d: 1 } }
deepClone(temp).then(obj => {
    console.log(obj)
})

_.cloneDeep(value)

_.cloneDeep()是lodash库所提供的深拷贝函数,也是个人比较推荐的一种,基本可以满足深拷贝的要求,不需要我们再自己写工具类。

自定义工具类deeepClone

最后当然得自己实现一个,但是一个完美的深拷贝函数要考虑的东西太多了,比如要不要考虑原型链上的属性、Dom对象如何处理、函数如何处理等,所以我提供了一个不太完善的深拷贝函数,这个函数对于对象方法属性的深拷贝也是想了比较久才写出来的。

function deepClone(obj, parent) {
  function isObj(obj) {
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
  }
  function isFunction(obj) {
    return typeof obj === 'function'
  }
  if(!isObj(obj)) {
    return obj
  }

  if(isFunction(obj)) {
    return obj.bind(parent)
  }

  const newObj = Array.isArray(obj) ? [...obj] : {...obj}
  
  Reflect.ownKeys(obj).forEach(key => {
    newObj[key] = isObj(newObj[key]) ? deepClone(newObj[key], newObj) : newObj[key]
  })
  
  return newObj
}

let temp = { a: 123, b: { c: function () {return 3} } }
let obj = deepClone(temp)

结语

每写一篇文章感觉都是对知识的总结提炼,虽然没人看也要坚持下去。

如果有疏漏的地方,欢迎指出

你可能感兴趣的:(浅谈JavaScript深拷贝)