SOURCE

let obj = {
    a: 1,
    b: {
        c: 2,
        d: 3
    },
    d: new RegExp(/^\s+|\s$/g)
}

let obj1 = {
    id: 1,
    name: "张三",
    gendar: 0,
    workflow: [
        {
            id: 1,
            name: '公司1',
            date: '2024-01-01',
            duty: []
        }, {
            id: 2,
            name: '公司2',
            date: '2024-02-01',
            duty: [
                {
                    id: 1,
                    dutyName: '公司2-职责1'
                },
                {
                    id: 2,
                    dutyName: '公司2-职责2'
                }
            ]
        }, {
            id: 3,
            name: '公司3',
            date: '2024-04-01',
            duty: [
                {
                    id: 1,
                    dutyName: '公司3-职责1'
                }
            ]
        }
    ],
    others: {
        a: 1,
        b: 2,
        c: 3,
    }
}

// 测试的obj对象
const objTest = {
    // =========== 1.基础数据类型 ===========
    num: 0, // number
    num1: new Number(0),
    str: '', // string
    str1: new String(""),
    bool: true, // boolean
    bool1: new Boolean(true),
    unf: undefined, // undefined
    nul: null, // null
    sym: Symbol('sym'), // symbol
    // bign: BigInt(1n), // bigint

    // =========== 2.Object类型 ===========
    // 普通对象
    obj: {
        name: '我是一个对象',
        id: 1
    },
    // 数组
    arr: [0, 1, 2],
    // 函数
    func: function () {
        console.log('我是一个函数')
    },
    // 日期
    date: new Date(0),
    // 正则
    reg: new RegExp('/我是一个正则/ig'),
    // Map
    map: new Map().set('mapKey', 1),
    // Set
    set: new Set().add('set'),
    // =========== 3.其他 ===========
    [Symbol('1')]: 1  // Symbol作为key
};

// 4.添加不可枚举属性
Object.defineProperty(objTest, 'innumerable', {
    enumerable: false,
    value: '不可枚举属性'
});

// 5.设置原型对象
Object.setPrototypeOf(objTest, {
    proto: 'proto'
})

// 6.设置loop成循环引用的属性
// objTest.loop = objTest


function deepClone(obj) {
    // 这种可以应付大部分常规数据
    if (typeof obj !== "object" || obj == null) return obj
    const clone_obj = Array.isArray(obj) ? [] : {}
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            clone_obj[key] = deepClone(obj[key])
        }
    }
    return clone_obj
}

function deepClone1(obj, map = new WeakMap()) {
    //使用 WeakMap 作为一个Hash表来进行查询 可以解决不能处理循环引用的问题
    if (obj instanceof RegExp) return new RegExp(obj);
    if (obj instanceof Date) return new Date(obj);

    if (obj == null || typeof obj != "object") return obj
    if (map.has(obj)) {
        return map.get(obj)
    }
    let t = new obj.constructor()
    map.set(obj, t)
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            t[key] = deepClone1(obj[key], map)
        }
    }
    return t
}

let clone_obj = deepClone1(objTest)
// obj.d = /^\s|[0-9]+$/g
objTest.str = "NMR"
// obj1.workflow[0].duty.push({ id: 1 })
console.log("拷贝对象:", clone_obj)
console.log("原对象:", objTest)

console.log(objTest === clone_obj)
console.log(objTest.func === clone_obj.func)
console.log(objTest.sym === clone_obj.sym)
console.log(objTest.innumerable ,clone_obj.innumerable)
console.log(clone_obj.proto)

function deepCloneTest(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 data
        }

        // 日期或者正则对象则直接构造一个新的对象返回
        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作为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)
}

console 命令行工具 X clear

                    
>
console