【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

原创文章,作者:端木书台,如若转载,请注明出处:https://blog.ytso.com/tech/webdev/274428.html

(0)
上一篇 2022年7月15日 00:35
下一篇 2022年7月15日 00:36

相关推荐

发表回复

登录后才能评论