Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手写题:深拷贝 #11

Open
Hongbusi opened this issue Jun 10, 2022 · 2 comments
Open

手写题:深拷贝 #11

Hongbusi opened this issue Jun 10, 2022 · 2 comments

Comments

@Hongbusi
Copy link
Member

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
}
@Hongbusi Hongbusi added handwritten-code today 每日一题。 labels Jun 10, 2022
@ConderL
Copy link

ConderL commented Jun 13, 2022

基于洪佬的深拷贝自己又实现了一遍,增加了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
Copy link

zyyv commented Jun 13, 2022

普通简单克隆 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
}

@Hongbusi Hongbusi removed the today 每日一题。 label Jun 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants