/** * Clone object and all it's properties. * Works recursively. * * @param {Object} obj Object to clone. * @param {Mixed} seen For internal use only. Don't set. * @return {Object} The clone. */ export const clone = (obj, seen) => { if (typeof obj !== 'object' || obj === null) { return obj; } seen = seen || new Map(); const lookup = seen.get(obj); if (lookup) { return lookup; } let newObj; let cloneDeep = false; if (!Array.isArray(obj)) { if (obj instanceof Date) { newObj = new Date(obj.getTime()); } else if (obj instanceof RegExp) { newObj = new RegExp(obj); } else { const proto = Object.getPrototypeOf(obj); if (proto && Object.isFrozen(obj)) { newObj = obj; } else { newObj = Object.create(proto); cloneDeep = true; } } } else { newObj = []; cloneDeep = true; } seen.set(obj, newObj); if (cloneDeep) { const keys = Object.getOwnPropertyNames(obj); for (let i = 0; i < keys.length; ++i) { const key = keys[i]; const descriptor = Object.getOwnPropertyDescriptor(obj, key); if (descriptor && (descriptor.get || descriptor.set)) { Object.defineProperty(newObj, key, descriptor); } else { newObj[key] = clone(obj[key], seen); } } } return newObj; };