js实现完整深拷贝、包括循环引用、symbol、set/map等

function deepClone(target) {
  // WeakMap作为记录对象Hash表(用于防止循环引用)
  const map = new WeakMap();

  // 判断是否为object类型的辅助函数,减少重复代码
  function isObject(target) {
    return (typeof target === "object" && target) || (typeof target === "function");
  }

  function clone(data) {

    // 基础类型直接返回值
    if (!isObject(data)) {
      return;
    }

    // 日期或者正则对象则直接构造一个新的对象返回
    if ([Date, RegExp].includes(data.constructor)) {
      return new data.constructor(data);
    }

    // 处理函数对象
    if (typeof data === "function") {
      return new Function("return " + data.toString())();
    }

    //如果该对象已经存在,则直接返回该对象
    const exist = map.get(data);
    if (exist) {
      return exist;
    }

    //处理map对象
    if (data instanceof Map) {
      const result = new Map();
      map.set(data, result);
      data.forEach((val, key) => {
        // map中的值为object的话也得深拷贝
        if (isObject(val)) {
          result.set(key, clone(val));
        } else {
          result.set(key, val);
        }
      });
      return result;
    }

    //处理set对象
    if (data instanceof Set) {
      const result = new Set();
      map.set(data, result);
      data.forEach(val => {
        // set中的值为object的话也得深拷贝
        if (isObject(val)) {
          result.add(clone(val));
        } else {
          result.add(val);
        }
      });
      return result;
    }

    //针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys()
    // 注:Reflect.ownKeys(obj)相当于[...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)]
    // 收集键名(考虑了以Symbol作为key以及不可枚举的属性)
    const keys = Reflect.ownKeys(data);

    // 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述
    const allDesc = Object.getOwnPropertyDescriptors(data);

    // 结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链, 这里得到的result是对data的浅拷贝
    const result = Object.create(Object.getPrototypeOf(data), allDesc);

    // 新对象加入到map中,进行记录
    map.set(data, result);

    // Object.create()是浅拷贝,所以要判断并递归执行深拷贝
    keys.forEach(key => {
      const val = data[key];
      if (isObject(val)) {
        // 属性值为 对象类型 或 函数对象 的话也需要进行深拷贝
        result[key] = clone(val);
      } else {
        result[key] = val;
      }
    });
    return result;
  }

  return clone(target);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值