TS代码实践 - 深拷贝

539 阅读1分钟

如果处理简单对象,可以使用 JSON

如果不考虑兼容性,可以使用 structuredClone

要点

  • 循环引用
  • 不同类型处理

代码

const argsTag = '[object Arguments]';
const arrayTag = '[object Array]';
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const mapTag = '[object Map]';
const numberTag = '[object Number]';
const objectTag = '[object Object]';
const regexpTag = '[object RegExp]';
const setTag = '[object Set]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const weakMapTag = '[object WeakMap]';

function _cloneDeep(value: any, stack = new Map()) {
  let result: any;

  // 原始类型直接返回
  if (!(value !== null && (typeof value === 'object' || typeof value === 'function'))) {
    return value;
  }

  // 处理循环引用
  if ([arrayTag, mapTag, objectTag, setTag].includes(Object.prototype.toString.call(value))) {
    if (stack.has(value)) {
      return stack.get(value);
    }
    stack.set(value, result);
  }

  switch (Object.prototype.toString.call(value)) {
    case boolTag:
    case dateTag:
    case numberTag:
    case stringTag:
      result = new value.constructor(value);
      break;

    case regexpTag:
      result = new value.constructor(value.source, value.flags);
      result.lastIndex = value.lastIndex;
      break;

    case mapTag:
      result = new value.constructor();
      (value as Map<any, any>).forEach((v, k) => {
        (result as Map<any, any>).set(k, _cloneDeep(v, stack));
      });
      break;

    case setTag:
      result = new value.constructor();
      (value as Set<any>).forEach((v) => {
        (result as Set<any>).add(_cloneDeep(v, stack));
      });
      break;

    case arrayTag:
      result = new value.constructor();
      for (let index = 0; index < value.length; index++) {
        result[index] = _cloneDeep(value[index], stack);
      }
      break;

    case objectTag:
      result = new value.constructor();
      Object.keys(value).forEach((k) => {
        result[k] = _cloneDeep(value[k], stack);
      });
      break;

    default:
      result = value;
      break;
  }

  return result;
}

function cloneDeep<T>(value: T): T {
  return _cloneDeep(value);
}

补充

lodash 实现了克隆 Symbol,我个人觉得 Symbol 代表独一无二的,不应该被克隆,就像函数一样直接返回。

参考