core_deep-copy.js

// 浅拷贝与深拷贝:
// 基本数据储存在栈中,引用类型数据储存在堆之中
// 基本类型数据可直接复制,引用类型数据在赋值时,新变量没有获得新值,而是将指针指向此对象的堆位置
// 浅拷贝:拷贝了对象的引用地址,没有获得新值;深拷贝:获得新值,而不是获得引用地址

// 浅拷贝:let a = b;let a = Object.assign({},b)
// 深拷贝:let a = JSON.parse(JSON.stringify(b));
// 考虑多种引用类型的完整深拷贝:
import { isPlainObject, getObjectType, objectType } from "../modules/type";

/**
 * @method deepCopy
 * @description 深拷贝
 * @param {Object} obj 需要拷贝的对象
 * @return {Object} 返回复制的对象
 * @example
 * const newObject = deepCopy(oldObject)
 */
export const deepCopy = function (origin, map = new WeakMap()) {
    // 一、无数据 或者 类型为基本类型、函数则直接返回
    if (!origin || !isPlainObject(origin)) {
        return origin;
    }

    // 二、获取引用数据类型
    const type = getObjectType(origin);

    // 三、已经存在,则直接返回,循环引用
    if (map.has(origin)) {
        return map.get(origin);
    }

    // 四、根据类型处理
    // 1,正则或者Date类型
    if (type === objectType.reg || type === objectType.date) {
        const newObject = new origin.constructor(origin.valueOf());
        map.set(newObject);
        return newObject;
    }
    // 2,Set 类型
    if (type === objectType.set) {
        const newObject = new Set();
        for (const value of origin) {
            newObject.add(deepCopy(value, map));
        }
        map.set(newObject);
        return newObject;
    }
    // 3,Map类型
    if (type === objectType.map) {
        const newObject = new Map();
        for (const [key, value] of origin) {
            newObject.set(key, deepCopy(value, map));
        }
        map.set(newObject);
        return newObject;
    }

    // 4,Array 或者 Object 类型
    // 考虑了以Symbol作为key的情况
    const keys = Reflect.ownKeys(origin);
    // 获取描述符
    const descriptors = Object.getOwnPropertyDescriptors(origin);
    // 考虑原型链,继承原对象的原型,描述符
    const newObject = Object.create(Object.getPrototypeOf(origin), descriptors);

    map.set(newObject);
    keys.forEach((key) => {
        const value = origin[key];
        newObject[key] = deepCopy(value, map);
    });

    // 数组类型则还原
    return type === objectType.array ? Array.from(newObject) : newObject;
};