【JavaScript】手写深拷贝 2.0(更新 2022-07-15)

前言

鄙人老版 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

posted @ 2022-07-15 00:06  努力挣钱的小鑫  阅读(75)  评论(0编辑  收藏  举报