developer-plus / interview

https://interview.developer-plus.org
MIT License
9 stars 1 forks source link

手写题:深拷贝 #11

Open Hongbusi opened 2 years ago

Hongbusi commented 2 years ago
function isObject(value) {
  const valueType = typeof value
  return (value !== null) && (valueType === 'object' || valueType === 'function')
}

function deepClone(originValue, map = new WeakMap()) {
  // 判断是否是一个 Set 类型
  if (originValue instanceof Set) {
    return new Set([...originValue])
  }

  // 判断是否是一个 Map 类型
  if (originValue instanceof Map) {
    return new Map([...originValue])
  }

  // 判断如果是 Symbol 的 value, 那么创建一个新的 Symbol
  if (typeof originValue === 'symbol') {
    return Symbol(originValue.description)
  }

  // 判断如果是函数类型, 那么直接使用同一个函数
  if (typeof originValue === "function") {
    return originValue
  }

  // 判断传入的 originValue 是否是一个对象类型
  if (!isObject(originValue)) {
    return originValue
  }
  if (map.has(originValue)) {
    return map.get(originValue)
  }

  // 判断传入的对象是数组, 还是对象
  const newObject = Array.isArray(originValue) ? []: {}
  map.set(originValue, newObject)
  for (const key in originValue) {
    newObject[key] = deepClone(originValue[key], map)
  }

  // 对Symbol的key进行特殊的处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  for (const sKey of symbolKeys) {
    // const newSKey = Symbol(sKey.description)
    newObject[sKey] = deepClone(originValue[sKey], map)
  }

  return newObject
}
ConderL commented 2 years ago

基于洪佬的深拷贝自己又实现了一遍,增加了RegExpDateMapSet的处理,平时没用过Buffer类型的数据就不进行处理了,代码可能不够完善有点问题,欢迎大佬们指正。

  function deepClone(originValue) {
    /**
     * 1.function、undefined、RegExp不会被解析
     * 2.循环引用会抛出错误
     * 如不考虑上述情况则可以使用此方法
     */
    return JSON.parse(JSON.stringify(originValue));
  }
  /**
   * 举个栗子:循环引用问题
   * const obj = {};
   * obj.obj = obj;
   * deepClone(obj); // Uncaught TypeError: Converting circular structure to JSON
   */

  /**
   * @description:
   * @param {*} originValue 源数据
   * @param { WeakMap } map 便于处理循环引用问题
   * @return {*} 深拷贝后的数据
   */
  function deepClone(originValue, map = new WeakMap()) {
    // 判断如果是 Symbol 的 value, 那么创建一个新的 Symbol
    if (typeof originValue === "symbol") {
      return Symbol(originValue.description);
    }

    // null、function、Date、简单数据类型则直接返回
    if (originValue === null || originValue instanceof Date || typeof originValue !== "object") {
      return originValue;
    }

    // 数据在weakMap中有引用则直接返回引用
    if (map.has(originValue)) {
      return map.get(originValue);
    }

    // 处理复杂数据类型Map、Set、Object、Array、RegExp

    if (originValue instanceof RegExp) {
      /**
       * 正则需处理lastIndex, 举个🌰
       * const reg = reg1 = /\d/g
       * reg.exec('123')
       * reg.lastIndex === reg1.lastIndex === 1 // true
       */
      const newValue = new originValue.constructor(originValue.source, originValue.flags);
      newValue.lastIndex = 0;
      return newValue;
    }

    // 前提条件,原生数据类型或拥有正确的构造器
    const bucket = new originValue.constructor();
    map.set(originValue, bucket);

    // 其余数据可以处理成可迭代类型, Reflect.ownKeys 相当于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
    const iterator = originValue.constructor === Object ? Reflect.ownKeys(originValue) : originValue;

    // 特殊情况: RegExp.prototype.exec 返回的数组, 比平时的数组多出input跟index属性
    if(Array.isArray(originValue) && originValue[0] && Object.prototype.hasOwnProperty.call(originValue, 'index')) {
      bucket.index = originValue.index
      bucket.input = originValue.input
    }

    for (const item of iterator) {
      let [key, value] = [];
      if (Array.isArray(item)) {
        [key, value] = item;
      } else if (originValue.constructor === Object) {
        [key, value] = [item, originValue[item]];
      } else {
        value = item;
      }
      setValue(bucket, key, deepClone(value, map));
    }

    return bucket;
  }

  function setValue(origin, key, value) {
    switch (origin.constructor) {
      case Map:
        return origin.set(key, value);
      case Set:
        return origin.add(value);
      case Array:
        return origin.push(value);
      default:
        origin[key] = value;
        break;
    }
  }
zyyv commented 2 years ago

普通简单克隆 Source Code

export function deepClone(origin: unknown): unknown {
  if (isArray(origin)) return origin.map(child => deepClone(child))

  if (isObject(origin)) {
    return Object.fromEntries(
      Object.entries(origin).map(([k, v]) => [k, deepClone(v)]),
    )
  }

  return origin
}

避免循环引用 Source Code

export function deepClone2(origin: any, hash = new WeakMap()): any {
  if (isObject(origin)) {
    if (hash.has(origin)) return hash.get(origin)

    const target: any = isArray(origin) ? [] : {}
    hash.set(origin, target)

    Object.entries(origin).forEach(([k, v]: [string, any]) => {
      if (isRegExp(v))
        target[k] = new RegExp(v)
      else if (isDate(v))
        target[k] = new Date(v)
      else
        target[k] = deepClone2(v, hash)
    })
    return target
  }
  return origin
}