你不容错过的JavaScript高级语法(深拷贝)

220 阅读2分钟

下面一篇文章我们来实现一下深拷贝。

深拷贝的介绍

通过定义的方法,拷贝出的对象和原来的对象没有关系。修改任何对象都不会相互影响。

通过JSON方法来处理深拷贝

    const info = JSON.parse(JSON.stringify(obj))

存在的问题:

  • 对于函数、Symbol等是无法处理的
  • 存在对象的循环引用,也会报错的

自定义深拷贝函数

  • 基本封装
    • 他只能处理基本数据类型, 对象, 数组。
    • 不能处理Symbol, Function, Set, Map类型
    function isObject(value) {
      const valueType = typeof value
      return (value !== null) && (valueType === "object" || valueType === "function")
    }

    function deepClone(originValue) {
      // 判断传入的originValue是否是一个对象类型
      if (!isObject(originValue)) {
        return originValue
      }

      // 判断传入的对象是数组, 还是对象
      const newObject = Array.isArray(originValue) ? []: {}
      for (const key in originValue) { // 如果originValue是一个函数(其实任何值都不会报错)in操作符也不会报错。
        newObject[key] = deepClone(originValue[key])
      }
      return newObject
    }
  • 处理函数类型。
      // 判断如果是函数类型, 那么直接使用同一个函数
      if (typeof originValue === "function") {
        return originValue
      }
  • 处理Symbol类型
    • Symbol作为值是可以直接拷贝的。
    • 由于for ... of不能遍历Symbol为key的对象,所以需要做特殊处理。
     // 判断如果是Symbol的value, 那么创建一个新的Symbol
      if (typeof originValue === "symbol") {
        return Symbol(originValue.description)
      }
      // 对Symbol的key进行特殊的处理
      const symbolKeys = Object.getOwnPropertySymbols(originValue)
      for (const sKey of symbolKeys) {
        newObject[sKey] = deepClone(originValue[sKey])
      }
  • 处理Set, Map类型
    • 注意: 这里只对Set,Map类型做浅拷贝。
      // 判断是否是一个Set类型
      if (originValue instanceof Set) {
        return new Set([...originValue])
      }

      // 判断是否是一个Map类型
      if (originValue instanceof Map) {
        return new Map([...originValue])
      }
  • 处理对象循环引用问题
    • 通过Map来实现,就是先把对象放进Map中。每次遍历,如果遇到循环引用,那么直接将newObject赋值给他。
    • 并且为了防止多次调用,自由变量Map中的值过多,应该将Map作为参数传递。
    function isObject(value) {
      const valueType = typeof value
      return (value !== null) && (valueType === "object" || valueType === "function")
    }

    function deepClone(originValue, map = new WeakMap()) {
      // 判断是否是一个Set类型
      if (originValue instanceof Set) {
        return new Set([...originValue])
      }

      // 判断是否是一个Map类型
      if (originValue instanceof Map) {
        return new Map([...originValue])
      }

      // 判断如果是Symbol的value, 那么创建一个新的Symbol
      if (typeof originValue === "symbol") {
        return Symbol(originValue.description)
      }

      // 判断如果是函数类型, 那么直接使用同一个函数
      if (typeof originValue === "function") {
        return originValue
      }

      // 判断传入的originValue是否是一个对象类型
      if (!isObject(originValue)) {
        return originValue
      }
      if (map.has(originValue)) {
        return map.get(originValue)
      }

      // 判断传入的对象是数组, 还是对象
      const newObject = Array.isArray(originValue) ? []: {}
      map.set(originValue, newObject)
      for (const key in originValue) {
        newObject[key] = deepClone(originValue[key], map)
      }

      // 对Symbol的key进行特殊的处理
      const symbolKeys = Object.getOwnPropertySymbols(originValue)
      for (const sKey of symbolKeys) {
        // const newSKey = Symbol(sKey.description)
        newObject[sKey] = deepClone(originValue[sKey], map)
      }

      return newObject
    }

xdm, 你不容错过的JavaScript高级语法专栏已经更文完毕,感谢大家的观看。