Skip to content

手写深拷贝

方法一,用JSON

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

这个方法有如下缺点:

  1. 不支持 Date 、正则 、undefined、函数等数据。
  2. 不支持引用(即环状结构)。

方法二,用递归

要点

  1. 递归
  2. 判断类型(首先判断基本类型、引用类型,然后引用类型在细分)
  3. 检查环(使用Map/weakMap检查,因为因为只有Map/weakMap的key可以使用对象,Object的key只能使用string/symbol)
  4. 不拷贝原型上的属性
javascript
/**
 * @desc 深拷贝
 * @param obj
 * @param cache {Object} 缓存不能全局,临时创建并递归传递
 */
function deepClone(obj, cache = new Map()) {
  // 区分普通类型和复杂引用类型
  if (obj instanceof Object) {
    if (cache.has(obj)) return cache.get(obj)
    let result
    if (obj instanceof Function) {
      if (obj.prototype) {
        result = function (...args) {
          return obj.apply(this, args)
        }
      } else {
        result = (...args) => {
          return obj.call(undefined, ...args)
        }
      }
    } else if (obj instanceof Date) {
      result = new Date(obj - 0)
    } else if (obj instanceof RegExp) {
      result = new RegExp(obj.source, obj.flags)
    } else if (obj instanceof Array) {
      result = []
    } else {
      result = {}
    }
    cache.set(obj, result)
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        result[key] = deepClone(obj[key], cache)
      }
    }
    return result
  } else {
    return obj
  }
}
javascript
function deepClone(obj, cache = new Map()) {
  // 处理基础类型(null、undefined、原始类型)
  if (obj == null || typeof obj !== 'object') return obj;

  // 检查环(循环引用)
  if (cache.has(obj)) return cache.get(obj);

  // 判断具体类型(支持更多类型如 Date、RegExp、Map、Set 等)
  const type = Object.prototype.toString.call(obj);
  let clone;

  switch (type) {
    case '[object Date]':
      clone = new Date(obj.getTime());
      break;
    case '[object RegExp]':
      clone = new RegExp(obj.source, obj.flags);
      break;
    case '[object Map]':
      clone = new Map();
      cache.set(obj, clone); // 提前缓存,避免循环引用
      for (const [key, value] of obj.entries()) {
        clone.set(deepClone(key, cache), deepClone(value, cache));
      }
      return clone;
    case '[object Set]':
      clone = new Set();
      cache.set(obj, clone);
      for (const value of obj.values()) {
        clone.add(deepClone(value, cache));
      }
      return clone;
    case '[object Function]':
      if (obj.prototype) {
        clone = function (...args) { return obj.apply(this, args); }
      } else {
        clone = (...args) => {
          return obj.call(undefined, ...args)
        }
      }
    default:
      clone = Array.isArray(obj) ? [] : {};
  }

  // 缓存当前对象,避免后续循环引用
  cache.set(obj, clone);

  // 遍历自身属性(不拷贝原型链上的属性)
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], cache); // 递归拷贝
    }
  }

  return clone;
}

参考链接

浅拷贝和深拷贝