const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const symbolTag = '[object Symbol]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';
const canForArr = ['[object Map]', '[object Set]', '[object Array]', '[object Object]']
const noForArr = ['[object Symbol]', '[object RegExp]', '[object Function]']
function checkType(target) { return Object.prototype.toString.call(target) }
function checkTemp(target) { const c = target.constructor return new c() }
function cloneFunction(func) {
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 cloneSymbol(targe) { return Object(Symbol.prototype.valueOf.call(targe)); }
function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
}
function deepClone(target, map = new Map()) {
const type = checkType(target)
if (!canForArr.concat(noForArr).includes(type)) return target
if (type === funcTag) return cloneFunction(target)
if (type === regexpTag) return cloneReg(target)
if (type === symbolTag) return cloneSymbol(target)
const temp = checkTemp(target)
if (map.get(target)) {
return map.get(target)
}
map.set(target, temp)
if (type === mapTag) {
target.forEach((value, key) => {
temp.set(key, deepClone(value, map))
})
return temp
}
if (type === setTag) {
target.forEach(value => {
temp.add(deepClone(value, map))
})
return temp
}
for (const key in target) {
temp[key] = deepClone(target[key], map)
}
return temp
}