@jsbits/deep-clone
Version:
Performs a deep cloning of an object own properties and symbols, with loosy or exact behavior.
202 lines (199 loc) • 6.1 kB
JavaScript
/*
@jsbits/deep-clone
@author aMarCruz
@version 1.1.1 ESM+ES6
@license MIT
*/
/* eslint-disable */
const _nodejs = (function () {
const g = typeof global === 'object' && global;
const m = g && g.process &&
typeof g.Buffer === 'function' && /^v?(\d+)/.exec(g.process.version);
return (m && m[1]) | 0;
})();
const _OP = Object.prototype;
const _toString = _OP.toString;
const _ownKeys = typeof Reflect === 'object' &&
typeof Reflect.ownKeys === 'function' && Reflect.ownKeys;
const clonable = {
Date: 1 ,
RegExp: 1 ,
String: 1 ,
Number: 1 ,
Boolean: 1 ,
Float32Array: 1 ,
Float64Array: 1 ,
Int8Array: 1 ,
Int16Array: 1 ,
Int32Array: 1 ,
Uint8Array: 1 ,
Uint8ClampedArray: 1 ,
Uint16Array: 1 ,
Uint32Array: 1 ,
Array: 2 ,
ArrayBuffer: 3 ,
SharedArrayBuffer: 3 ,
DataView: 4 ,
Error: 6 ,
Map: 5 ,
Set: 5 ,
Arguments: 7 ,
Atomics: 8 ,
JSON: 8 ,
Math: 8 ,
Promise: 8 ,
WeakMap: 8 ,
WeakSet: 8 ,
XMLHttpRequest: 8 ,
};
const arrayLike = [
'Array',
'String',
'Float32Array',
'Float64Array',
'Int8Array',
'Int16Array',
'Int32Array',
'Uint8Array',
'Uint8ClampedArray',
'Uint16Array',
'Uint32Array',
];
const getKeyGetter = function (exact) {
const _keys = exact ? Object.getOwnPropertyNames : Object.keys;
const _getSymbols = Object.getOwnPropertySymbols;
if (!_getSymbols) {
return _keys;
}
if (exact) {
return (obj) => _keys(obj).concat(_getSymbols(obj));
}
const _isEnum = _OP.propertyIsEnumerable;
return (obj) => {
const objkeys = _keys(obj);
const symbols = _getSymbols(obj);
for (let i = 0; i < symbols.length; i++) {
if (_isEnum.call(obj, symbols[i])) {
objkeys.push(symbols[i]);
}
}
return objkeys;
};
};
const getKeysFac = function (exact) {
const _filtIdx = (prop) => prop !== '0' && (prop | 0) <= 0 && prop !== 'length';
const _getKeys = exact && _ownKeys || getKeyGetter(exact);
return (obj, type) => {
const objkeys = _getKeys(obj);
return ~arrayLike.indexOf(type) ? objkeys.filter(_filtIdx) : objkeys;
};
};
const cloneMapOrSet = (src, fn, type) => {
const dest = new src.constructor();
const cb = type === 'Set'
? function (v) { this.add(fn(v)); }
: function (v, k) { this.set(fn(k), fn(v)); };
src.forEach(cb, dest);
return dest;
};
const cloneDataView = (src) => {
const buffer = src.buffer.slice(0);
return new src.constructor(buffer, src.byteOffset, src.byteLength);
};
const cloneError = (src) => {
const err = new src.constructor(src.message);
return Object.defineProperty(err, 'stack', {
value: src.stack, configurable: true, writable: true,
});
};
const cloneArguments = (src) => {
const args = Object.create(null);
return Object.defineProperty(args, 'length', {
value: src.length, configurable: true, writable: true,
});
};
const cloneArray = (src, fn) => src.map(fn);
const cloneFn = {
[8 ]: (obj) => obj,
[7 ]: cloneArguments,
[2 ]: cloneArray,
[3 ]: (obj) => obj.slice(0),
[4 ]: cloneDataView,
[6 ]: cloneError,
[5 ]: cloneMapOrSet,
};
const createObject = (obj, type, fn) => {
const cloneType = clonable[type];
if (cloneType === 1 ) {
return obj.slice && _nodejs && Buffer.isBuffer(obj)
? obj.slice(0)
: new obj.constructor(obj.valueOf());
}
if (cloneFn[cloneType]) {
return cloneFn[cloneType](obj, fn, type);
}
return type.lastIndexOf(' Iterator', type.length - 9) > -1
? obj : Object.create(Object.getPrototypeOf(obj));
};
const getObjectType = (function () {
const _getTag = (obj) => _toString.call(obj).slice(8, -1);
if (!_nodejs || _nodejs >= 5) {
return _getTag;
}
return (obj) => {
const tag = _getTag(obj);
return tag === 'Object' && obj.constructor && obj.constructor.name === 'Promise'
? 'Promise' : tag;
};
})();
const cloneFac = function (getKeys) {
const _clone = function _clone(obj) {
if (!obj || typeof obj !== 'object') {
return obj;
}
const type = getObjectType(obj);
const clone = createObject(obj, type, _clone);
const props = getKeys(obj, type);
for (let i = 0; i < props.length; i++) {
const prop = props[i];
const desc = Object.getOwnPropertyDescriptor(obj, prop);
if (desc.value !== undefined) {
desc.value = _clone(obj[prop]);
}
Object.defineProperty(clone, prop, desc);
}
return clone;
};
return _clone;
};
const looseClone = cloneFac(getKeysFac(false));
const exactClone = cloneFac(getKeysFac(true));
/**
* Performs a deep cloning of an object own properties and symbols, preserving
* its prototype.
*
* By default `cloneObject` works in "loosy mode", where it clones only
* the object _enumerable_ properties and symbols.
*
* To enable the "exact mode" and clone all, pass `true` in the second parameter.
*
* Both modes retain all the attributes of the copied properties (enumerable,
* configurable, writable) and correctly transfer the `get` and/or `set`
* methods, although these, like the other function-type values,
* _are copied by reference_.
*
* Try to limit the usage of this function to POJOs, as this function does not
* work for objects with constructor that requires parameters (other than
* the most JS built-in Objects), nor objects with recursive references.
*
* @template T
* @param {T} value Value to clone, mostly an object or array.
* @param {boolean} [exact=false] If `true`, also clone the non-enumerable properties
* @returns {T} The cloned object or value.
* @since 1.0.0
*/
const deepClone = function _deepClone(value, exact) {
return exact ? exactClone(value) : looseClone(value);
};
export default deepClone;
//# sourceMappingURL=index.mjs.map