export default function deepClone(value, seen = new WeakMap()) {
if (value === null || typeof value !== 'object') {
return value;
}
if (seen.has(value)) {
return seen.get(value);
}
const tag = Object.prototype.toString.call(value);
if (tag === '[object Date]') {
return (new Date(value.getTime()));
}
if (tag === '[object RegExp]') {
const clonedRegExp = new RegExp(value.source, value.flags);
clonedRegExp.lastIndex = value.lastIndex;
return (clonedRegExp);
}
if (tag === '[object Map]') {
const clonedMap = new Map();
seen.set(value, clonedMap);
for (const [key, val] of value.entries()) {
clonedMap.set(deepClone(key, seen), deepClone(val, seen));
}
return (clonedMap);
}
if (tag === '[object Set]') {
const clonedSet = new Set();
seen.set(value, clonedSet);
for (const item of value.values()) {
clonedSet.add(deepClone(item, seen));
}
return (clonedSet);
}
if (tag === '[object ArrayBuffer]') {
return (value.slice(0));
}
if (ArrayBuffer.isView(value)) {
if (tag === '[object DataView]') {
const bufferClone = deepClone(value.buffer, seen);
return (
new DataView(bufferClone, value.byteOffset, value.byteLength)
);
}
return (new value.constructor(value));
}
if (tag === '[object Error]') {
const clonedError = new value.constructor(value.message);
seen.set(value, clonedError);
copyOwnProperties(value, clonedError, seen);
return (clonedError);
}
const cloned = Array.isArray(value)
? new Array(value.length)
: Object.create(Object.getPrototypeOf(value));
seen.set(value, cloned);
copyOwnProperties(value, cloned, seen);
return (cloned);
}
function copyOwnProperties(source, target, seen) {
for (const key of Reflect.ownKeys(source)) {
const descriptor = Object.getOwnPropertyDescriptor(source, key);
if (!descriptor) {
continue;
}
if ('value' in descriptor) {
descriptor.value = deepClone(descriptor.value, seen);
}
Object.defineProperty(target, key, descriptor);
}
}