53 lines
1.3 KiB
JavaScript
53 lines
1.3 KiB
JavaScript
|
/**
|
||
|
* 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] = this._clone(obj[key], seen);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return newObj;
|
||
|
};
|