前端小技巧: 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))
    
  • 总结

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值