• 【JavaScript】手写深拷贝 2.0(更新 20220715)


    前言

    鄙人老版 js 深拷贝博客链接,当时写的存在很多不足...现在跟着方应杭老师复习了下,收获满满。

    用 JSON

    const b = JSON.parse(JSON.stringify(a))
    

    缺点:

    1. 不支持 Date、RegExp(正则)、函数等数据;

    2. 不支持引用(即环状结构,类似 window.self = window)。

    用递归

    基础版

    支持 Date、RegExp(正则)、函数等引用数据的拷贝。

    const deepClone = (a) => {
      let res = undefined
    
      // 数据类型判断
      if (a instanceof Object) {
        // 类 判断
        if (a instanceof Function) {
          // 箭头函数判断
          if (a.prototype) {
            // 普通函数
            res = function (...args) {
              return a.call(this, ...args)
            }
          } else {
            // 箭头函数
            res = (...args) => {
              return a.call(undefined, ...args)
            }
          }
        } else if (a instanceof Array) {
          res = []
        } else if (a instanceof Date) {
          res = new Date(a - 0) // 日期格式 - 0 自动转换为时间戳
        } else if (a instanceof RegExp) {
          res = new RegExp(a)
        } else {
          res = {}
        }
    
        // 递归
        for (let k in a) {
          if (a.hasOwnProperty(k)) {
            res[k] = deepClone(a[k])
          }
        }
      } else {
        res = a
      }
      
      return res
    }
    

    测试结果:

    image

    完整版(支持引用自身的情况)

    比如浏览器的 window.self = window,这个时候如果还用上面的拷贝就会导致无限递归导致栈溢出报错。

    image

    正确的做法是添加一个 map 来记录每次拷贝过的数据,如果出现重复的就不再进行拷贝和递归。(ps:为什么用 map?因为对象 key 值只能为字符串)

    const deepClone = (a, cache) => {
      let res = undefined
      if(!cache){
        cache = new Map() // 缓存不能全局,最好临时创建并递归传递
      }
    
      if (a instanceof Object) {
        // 每次拷贝前判断前面是否已经拷贝过
        // 如果出现 a.self = a 在这里就会返回
        // 防止后续无限递归导致栈溢出
        if (cache.get(a)) {
          return cache.get(a)
        }
    
        if (a instanceof Function) {
          if (a.prototype) {
            res = function (...args) {
              return a.call(this, ...args)
            }
          } else {
            res = (...args) => {
              return a.call(undefined, ...args)
            }
          }
        } else if (a instanceof Array) {
          res = []
        } else if (a instanceof Date) {
          res = new Date(a - 0)
        } else if (a instanceof RegExp) {
          res = new RegExp(a)
        } else {
          res = {}
        }
    
        // 每次递归前将拷贝的值就存入 map
        cache.set(a, res)
    
        for (let k in a) {
          if (a.hasOwnProperty(k)) {
            // 通过参数传递缓存 map
            res[k] = deepClone(a[k], cache)
          }
        }
      } else {
        res = a
      }
      
      return res
    }
    

    测试结果:

    image

  • 相关阅读:
    Linux上查找
    Linux进程
    Linux重定向
    Linux上常用的基本命令
    LInux上返回到切换目录前的目录
    【网络知识之一】4/7层网络模型
    【操作系统之十五】iptables黑白名单、自定义链、网络防火墙、常用动作
    【操作系统之十四】iptables扩展模块
    【操作系统之十三】Netfilter与iptables
    【操作系统之十二】分支预测、CPU亲和性(affinity)
  • 原文地址:https://www.cnblogs.com/cqkjxxxx/p/16479846.html
Copyright © 2020-2023  润新知