SOURCE

// 可以继续遍历的类型
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const deepTag = [mapTag, setTag, arrayTag, objectTag]
// 不可遍历类型
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const numberTag = '[object Number]';
const regexpTag = '[object RegExp]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const funcTag = '[object Function]'


// 使用while替换fon..in
function foreach(arr, iterate) {
    let index = -1
    let len = arr.length
    while (++index < len) {
        iterate(arr[index], index)
    }
}

// 判断数据类型
function getType(target) {
    return Object.prototype.toString.call(target)
}
//初始化数据
function getInit(target) {
    const Ctor = target.constructor
    return new Ctor()
}
// 判断对象类型
function isObject(target) {
    const type = typeof target
    return target !== null && (type === 'object' || type === 'function')
}
// 其他类型克隆
function cloneOtherType(target, type) {
    const Ctor = target.constructor
    switch (type) {
        case boolTag:
        case errorTag:
        case numberTag:
        case stringTag:
        case dateTag:
            return new Ctor(target)
        case regexpTag:
            return cloneReg(target)
        case symbolTag:
            return cloneSymbol(target)
        case funcTag:
            return cloneFunction(target)
        default:
            return null
    }
}
function cloneReg(target) {
    const flag = /\w*$/
    const result = new target.constructor(target.source, flag.exec(target))
    result.lastIndex = target.lastIndex
    return result
}
function cloneSymbol(target) {
    // return Object(Symbol.prototype.valueOf.call(target));
    return Symbol.prototype.valueOf.call(target)
}
function cloneFunction(func) {
    const bodyReg = /\{([\s\S]*)\}$/;
    // const bodyReg = /(?<={)(.|\n)+(?=})/m;
    const paramReg = /(?<=\().+(?=\)\s+{)/;
    const funcString = func.toString();
    if (func.prototype) {
        console.log('普通函数');
        const param = paramReg.exec(funcString);
        const body = bodyReg.exec(funcString);
        if (body) {
            if (param) {
                const paramArr = param[0].split(',');
                return new Function(...paramArr, body[0]);
            } else {
                return new Function(body[0]);
            }
        } else {
            return null;
        }
    } else {
        return eval(funcString);
    }
}
function deepClone(target, map = new WeakMap()) {
    if (!isObject(target)) {
        return target
    }
    const type = getType(target)
    let cloneTarget
    if (deepTag.includes(type)) {
        cloneTarget = getInit(target)
    } else {
        return cloneOtherType(target, type)
    }

    if (map.get(target)) {
        return target
    }
    map.set(target, cloneTarget)

    // 克隆set
    if (type === setTag) {
        target.forEach(value => {
            cloneTarget.add(deepClone(value));
        });
        return cloneTarget;
    }
    // map
    if (type === mapTag) {
        target.forEach((value, key) => {
            cloneTarget.set(key, deepClone(value))
        })
        return cloneTarget
    }
    // 对象或数组
    for (let key in target) {
        cloneTarget[key] = deepClone(target[key], map);
    }
    return cloneTarget
}



// 测试数据
const map = new Map();

map.set('key', 'value');

map.set('demo', '测试数据');


const set = new Set();

set.add('demo');

set.add('测试数据');


const target = {
    field1: 1,
    field2: undefined,
    field3: {
        child: 'child'
    },
    field4: [2, 4, 8],
    empty: null,
    map,
    set,
    bool: new Boolean(true),
    num: new Number(2),
    str: new String(2),
    symbol: Object(Symbol(1)),
    date: new Date(),
    reg: /\d+/,
    // error: new Error(),
    func1: () => {
        console.log('code秘密花园');
    },
    func2: function (a, b) {
        return a + b;
    }
};

console.time('测试');
var clone = deepClone(target)
console.log(clone.func2(1, 2))
// console.log(target)
console.timeEnd('测试');
console 命令行工具 X clear

                    
>
console