const mapTag = '[object Map]'; const setTag = '[object Set]'; const arrayTag = '[object Array]'; const objectTag = '[object Object]'; const argsTag = '[object Arguments]'; const boolTag = '[object Boolean]'; const dateTag = '[object Date]'; const numberTag = '[object Number]'; const stringTag = '[object String]'; const symbolTag = '[object Symbol]'; const errorTag = '[object Error]'; const regexpTag = '[object RegExp]'; const funcTag = '[object Function]'; const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]; function forEach(array: Array, iteratee: any) { let index = -1; const length = array.length; while (++index < length) { iteratee(array[index], index); } return array; } function isObject(target: any) { const type = typeof target; return target !== null && (type === 'object' || type === 'function'); } function getType(target: any) { return Object.prototype.toString.call(target); } function getInit(target: any) { const Ctor = target.constructor; return new Ctor(); } function cloneSymbol(targe: any) { return Object(Symbol.prototype.valueOf.call(targe)); } function cloneReg(targe: any) { const reFlags = /\w*$/; const result = new targe.constructor(targe.source, reFlags.exec(targe)); result.lastIndex = targe.lastIndex; return result; } function cloneFunction(func: any) { const bodyReg = /(?<={)(.|\n)+(?=})/m; const paramReg = /(?<=\().+(?=\)\s+{)/; const funcString = func.toString(); if (func.prototype) { 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 cloneOtherType(targe: any, type: any) { const Ctor = targe.constructor; switch (type) { case boolTag: case numberTag: case stringTag: case errorTag: case dateTag: return new Ctor(targe); case regexpTag: return cloneReg(targe); case symbolTag: return cloneSymbol(targe); case funcTag: return cloneFunction(targe); default: return null; } } function clone(target: any, map = new WeakMap()) { // 克隆原始类型 if (!isObject(target)) { return target; } // 初始化 const type = getType(target); let cloneTarget: any; if (deepTag.includes(type)) { cloneTarget = getInit(target); } else { return cloneOtherType(target, type); } // 防止循环引用 if (map.get(target)) { return map.get(target); } map.set(target, cloneTarget); // 克隆set if (type === setTag) { target.forEach((value: any) => { cloneTarget.add(clone(value, map)); }); return cloneTarget; } // 克隆map if (type === mapTag) { target.forEach((value: any, key: any) => { cloneTarget.set(key, clone(value, map)); }); return cloneTarget; } // 克隆对象和数组 const keys = type === arrayTag ? undefined : Object.keys(target); forEach(keys || target, (value: any, key: any) => { if (keys) { key = value; } cloneTarget[key] = clone(target[key], map); }); return cloneTarget; } export default clone; // https://github.com/ConardLi/ConardLi.github.io/blob/master/demo/deepClone/src/clone_6.js#L1 // https://juejin.cn/post/6844903929705136141