Skip to content

深拷贝 && 浅拷贝


参考链接:

掘金:轻松拿下JS浅拷贝、深拷贝

深拷贝和浅拷贝的区别:

浅拷贝只进行一层复制,深层次的引用类型还是共享内存地址,原对象和拷贝对象还是会互相影响。

深拷贝无限层级拷贝,深拷贝后的原对不会和拷贝对象相互影响。

浅拷贝的实现:

  • Object.assign({}, obj)
  • arr.slice(0)
  • [].concat(arr)
  • Array.from(arr)
  • [...arr]{...obj}

深拷贝的实现:

要求:

  • 支持对象、数组、日期、正则的拷贝。
  • 处理原始类型(原始类型直接返回,只有引用类型才有拷贝这个概念)。
  • 处理Symbol作为键名的情况。
  • 处理函数(函数直接返回,拷贝函数没有意义,两个对象使用内存中同一个地址的函数,问题不大)
  • 处理DOM元素(DOM元素直接返回,拷贝DOM元素没有意义,都是指向页面中的同一个)
  • 额外开辟一个存储空间的WeakMap,解决循环引用递归爆栈问题(引入WeakMap的另一个意义,配合垃圾回收机制,防止内存泄露)
javascript
function deepClone (target, hash = new WeakMap()) { // 额外开启一个空间WeakMap来存储当前对象
	if (target === null) return target // 如果是null,不进行拷贝操作
  if (target instanceof Date) return new Date(target) // 处理日期
  if (target instanceof RegExp) return new RegExp(target) // 处理正则
  if (target instanceof HTMLElement) return target // 处理DOM元素
  if (typeof target !== 'object') return target // 如果是原始类型和函数,直接返回
  
  // 是引用类型的话就进行深拷贝
  if (hash.get(target)) return hash.get(target) // 拷贝当前对象时,先去存储空间中找,如果有直接返回
  const cloneTarget = new target.constructor() // 创建一个新的克隆对象或克隆数组
  hash.set(target, cloneTarget) // 如果存储空间中国没有就存进hash
  
  // 递归拷贝属性
  Reflect.ownKeys(target).forEach(key => { // 引入Reflect.ownKeys,处理Symbol作为键名的情况
  	cloneTarget[key] = deepClone(target[key], hash) // 递归拷贝每一层
  })
  return cloneTarget
}

JSON.parse(JSON.stringify(obj))

弊端:

  • 忽略undefined、symbol、函数
  • NaN、Infinity、-Infinity会被序列化为null
  • 无法解决循环引用的问题

未来的深拷贝

MDN:structuredClone