前端小技巧: TS实现深拷贝函数,考虑 Map, Set, 循环引用

写一个深拷贝函数

  • 使用 JSON.stringify 和 parse 这类只能适合数据结构简单的,不能循环引用的,没有function的

  • 普通深拷贝

    • 只考虑 Object Array

    • 无法转换 Map Set 和 循环引用

      function cloneDeep(obj: any) {
        if (typeof obj !== 'object' || !obj) return obj
        const result = obj instanceof Array ? [] : {}
        for (let key  in obj) {
          if (obj.hasOwnProperty(key)) {
            result[key] = cloneDeep(obj[key]) // 递归调用
          }
        }
        return result
      }
      
      // 不支持的场景
      const a: any = {
        set: new Set([10, 20, 30]),
        map: new Map(['x', 10], ['y', 20])
      }
      
      a.self = a // 循环引用 Uncaught RangeError: Maximum call stack size exceeded
      console.log(cloneDeep(a)) // 无法处理 Map Set 和 循环引用
      
  • 上述,对set和map的数据结构不行,会把他们干掉

  • 如果添加循环引用的数据,则直接报错

  • 好的方案如下:

    export function cloneDeep(obj: any, map = new WeakMap()): any {
      if (typeof obj !== 'object' || !obj) return obj
      // 避免循环引用
      const objFromMap = map.get(obj)
      if (objFromMap) return objFromMap
      let target: any = {}
      map.set(obj, target)
    
      // Map
      if (obj instanceof Map) {
        target = new Map()
        obj.forEach((v, k) => {
          const v1 = cloneDeep(v, map)
          const k1 = cloneDeep(k, map)
          target.set(k1, v1)
        })
      }
    
      // Set
      if (obj instanceof Set) {
        target = new Set()
        obj.forEach(v => {
          const v1 = cloneDeep(v, map)
          target.add(v1)
        })
      }
    
      // Array
      if (obj instanceof Array) {
        target = obj.map(item => cloneDeep(item, map))
      }
    
      // Object
      for (const key in obj) {
        const val = obj[key]
        target[key] = cloneDeep(val, map)
      }
      return target
    }
    
    const a: any = {
      set: new Set([10, 20, 30]),
      map: new Map(['x', 10], ['y', 20])
    }
    
    a.self = a // 循环引用 
    console.log(cloneDeep(a))
    
  • 总结

    • 功能完整性:考虑多种数据结构
    • 鲁棒性,考虑循环引用
    • 能否考虑到如上几点

你可能感兴趣的:(Typescript,Web,前端,深拷贝,循环引用)