js速记--深拷贝与浅拷贝

42 阅读2分钟

一、区别

  • 浅拷贝是指只复制对象的第一层,深处属性修改,会影响原来的对象
  • 深拷贝是指对对象所有层级属性都进行复制

二、浅拷贝的实现

  • Object.assign()
  • lodash库里的clone()方法
  • 展开运算符...
  • 还有一些数组的方法,比如 Array.prototype.concat()、Array.prototype.slice()等

三、深拷贝的实现

1、JSON方法实现

先通过JSON.string()转为字符串,然后再用JSON.parse()转成对象

var obj = {
    a: {
        b: '1'
    }
}
// 可以得到一个全新的拷贝的对象
JSON.parse(JSON.stringify(obj))

这种方法实现简单,但是存在一个问题:对象的属性只支持string、number、boolean、null、object、array,不支持其他类型,如果存在其他类型的属性,在序列化后会丢失属性或者被转为原始值

image.png

上面的对象,转换后,丢失了undefined和函数,日期对象被转换成了字符串格式的日期值。除此之外,正则对象、Symbol等也会被丢失。

其他的通过js原生方法实现的效果都和这个类似,只要涉及到序列化反序列化的过程,都存在同样的问题。

2、lodash库的 cloneDeep()

这个方法会返回一个全新的对象,完全拷贝原始对象的属性

const _ = require('lodash')
var obj = {
    date: new Date(),
    undef: undefined
}

var res = _.cloneDeep(obj)
// [object Date]
Object.prototype.toString.call(res.date)
// true
res.undef === undefined

3、structuredClone()

structuredClone() 是一个全局方法,可以复制对象

var res = structuredClone({date: new Date(), undef: undefined})

这个方法实现了对undefined、Date、RegExp等对象的拷贝,但是对Function、自定义类型、DOM节点、Error等还是无法拷贝,会抛出异常

4、手动实现深拷贝

思路:

  • 如果是基础数据类型,则直接返回
  • 如果是正则对象、日期对象,则调用相应的构造函数再构建一个
  • 剩余对象,可以先获取该对象的构造函数,然后重新创建一个实例,并将原对象的自身的属性拷贝一份赋给新创建的实例

存在的问题与解决方案:

  • 对象属性可能存在循环引用的问题,所以可以缓存一下所有创建过的对象,如果拷贝的时候发现对象已经被缓存过,可以直接返回缓存的值
function deepClone(obj) {
  if(typeof obj !== 'object' || obj === null) {
    return obj
  }
  if(obj instanceof RegExp) {
    return new RegExp(obj)
  }
  if(obj instanceof Date) {
    return new Date(obj)
  }
  // 剩下的对象,获取构造函数重新构建一个实例
  if(map.has(obj)) {
    return map.get(obj)
  }
  const target = new obj.constructor()
  // 复制对象自身属性,深拷贝
  Object.keys(obj).forEach((key) => {
    target[key] = deepClone(obj[key])
  })
  map.set(obj, target)
  return target
}