@jsbits/deep-clone
Version:
Performs a deep cloning of an object own properties and symbols, with loosy or exact behavior.
288 lines • 9.17 kB
JavaScript
;
/// <reference lib="es2015" />
var _a;
var _nodejs = (function () {
var g = typeof global === 'object' && global;
var m = g && g.process &&
typeof g.Buffer === 'function' && /^v?(\d+)/.exec(g.process.version);
return (m && m[1]) | 0;
})();
var _OP = Object.prototype;
var _toString = _OP.toString;
var _ownKeys = typeof Reflect === 'object' &&
typeof Reflect.ownKeys === 'function' && Reflect.ownKeys;
/**
* Clonable types with special handlers
*/
var clonable = {
Date: 1 /* Simple */,
RegExp: 1 /* Simple */,
String: 1 /* Simple */,
Number: 1 /* Simple */,
Boolean: 1 /* Simple */,
Float32Array: 1 /* Simple */,
Float64Array: 1 /* Simple */,
Int8Array: 1 /* Simple */,
Int16Array: 1 /* Simple */,
Int32Array: 1 /* Simple */,
Uint8Array: 1 /* Simple */,
Uint8ClampedArray: 1 /* Simple */,
Uint16Array: 1 /* Simple */,
Uint32Array: 1 /* Simple */,
Array: 2 /* Array */,
ArrayBuffer: 3 /* ArrayBuffer */,
SharedArrayBuffer: 3 /* ArrayBuffer */,
DataView: 4 /* DataView */,
Error: 6 /* Error */,
Map: 5 /* MapOrSet */,
Set: 5 /* MapOrSet */,
Arguments: 7 /* Arguments */,
Atomics: 8 /* AsIs */,
JSON: 8 /* AsIs */,
Math: 8 /* AsIs */,
Promise: 8 /* AsIs */,
WeakMap: 8 /* AsIs */,
WeakSet: 8 /* AsIs */,
XMLHttpRequest: 8 /* AsIs */,
};
var arrayLike = [
'Array',
'String',
'Float32Array',
'Float64Array',
'Int8Array',
'Int16Array',
'Int32Array',
'Uint8Array',
'Uint8ClampedArray',
'Uint16Array',
'Uint32Array',
];
/**
* Creates a function to get enumerable symbol names of an object.
*
* The array is returned as string[] because the lack of full typings
* for Symbol keys in TS v3
*
* @param {boolean} exact All keys?
* @returns {Function} Extractor
* @private
*/
var getKeyGetter = function (exact) {
// istanbul ignore next
var _keys = exact ? Object.getOwnPropertyNames : Object.keys;
var _getSymbols = Object.getOwnPropertySymbols;
// istanbul ignore next: until we can test IE11
if (!_getSymbols) {
return _keys;
}
// All the keys, including the non-enumerable ones
// istanbul ignore if: until we can test IE11
if (exact) {
return function (obj) { return _keys(obj).concat(_getSymbols(obj)); };
}
// Only enumerable keys and symbols
var _isEnum = _OP.propertyIsEnumerable;
return function (obj) {
var objkeys = _keys(obj);
var symbols = _getSymbols(obj);
for (var i = 0; i < symbols.length; i++) {
if (_isEnum.call(obj, symbols[i])) {
objkeys.push(symbols[i]);
}
}
return objkeys;
};
};
/**
* Creates a function to get the properties and symbol names of an object.
*
* @param {boolean} exact Include non-enum keys?
* @returns {Function} Extractor
* @private
*/
var getKeysFac = function (exact) {
// Avoid creating multiple anonymous functions
var _filtIdx = function (prop) { return prop !== '0' && (prop | 0) <= 0 && prop !== 'length'; };
var _getKeys = exact && _ownKeys || getKeyGetter(exact);
return function (obj, type) {
var objkeys = _getKeys(obj);
return ~arrayLike.indexOf(type) ? objkeys.filter(_filtIdx) : objkeys;
};
};
/**
* Clone the Map or Set and copy its elements.
*
* @param {Map|Set} src Object to copy
* @param {Function} fn Cloner
* @param {string} type Type of object
* @returns {Map|Set} The clone
* @private
*/
var cloneMapOrSet = function (src, fn, type) {
var dest = new src.constructor();
// IE11 will not copy items, anyway, we need clone keys and values
var cb = type === 'Set'
? function (v) { this.add(fn(v)); }
: function (v, k) { this.set(fn(k), fn(v)); };
src.forEach(cb, dest);
return dest;
};
/**
* Clone the given DataView object.
*
* @param {DataView} src Object to clone
* @returns {DataView} Clone
* @private
*/
var cloneDataView = function (src) {
var buffer = src.buffer.slice(0);
return new src.constructor(buffer, src.byteOffset, src.byteLength);
};
var cloneError = function (src) {
var err = new src.constructor(src.message);
return Object.defineProperty(err, 'stack', {
value: src.stack, configurable: true, writable: true,
});
};
/**
* Copies arguments to an object without prototype - Adds the length property.
*
* @param {Arguments} src arguments pseudo-array
* @returns {object} -
* @private
*/
var cloneArguments = function (src) {
var args = Object.create(null);
return Object.defineProperty(args, 'length', {
value: src.length, configurable: true, writable: true,
});
};
// Faster array cloning
var cloneArray = function (src, fn) { return src.map(fn); };
var cloneFn = (_a = {},
_a[8 /* AsIs */] = function (obj) { return obj; },
_a[7 /* Arguments */] = cloneArguments,
_a[2 /* Array */] = cloneArray,
_a[3 /* ArrayBuffer */] = function (obj) { return obj.slice(0); },
_a[4 /* DataView */] = cloneDataView,
_a[6 /* Error */] = cloneError,
_a[5 /* MapOrSet */] = cloneMapOrSet,
_a);
/**
* Creates a new object intance of the given type.
*
* @param {object} obj Non-null object
* @param {string} type Object type
* @param {Function} fn Cloner
* @returns {object} The new instance
* @private
*/
var createObject = function (obj, type, fn) {
var cloneType = clonable[type];
if (cloneType === 1 /* Simple */) {
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));
};
/**
* Get the object type, taking care about node <6 returning 'Object' for
* Promise.
*/
var getObjectType = (function () {
var _getTag = function (obj) { return _toString.call(obj).slice(8, -1); };
// istanbul ignore else
if (!_nodejs || _nodejs >= 5) {
return _getTag;
}
// istanbul ignore next
return function (obj) {
var tag = _getTag(obj);
return tag === 'Object' && obj.constructor && obj.constructor.name === 'Promise'
? 'Promise' : tag;
};
})();
/**
* Factory to create a "clone" function for loosy or exact mode.
*
* @param {Function} getKeys Keys & symbols extractor
* @returns {Function} Cloner
* @private
*/
// codebeat:disable[ABC,BLOCK_NESTING]
var cloneFac = function (getKeys) {
var _clone = function _clone(obj) {
// Filter out null, undefined, NaN, primitive values, and functions
if (!obj || typeof obj !== 'object') {
return obj;
}
// The type also allows optimize the getKeys function.
var type = getObjectType(obj);
// Get a new object of the same type and the properties of the source
var clone = createObject(obj, type, _clone);
var props = getKeys(obj, type);
for (var i = 0; i < props.length; i++) {
var prop = props[i];
var desc = Object.getOwnPropertyDescriptor(obj, prop);
// NOTE: `value` must be excluded for setter/getter
if (desc.value !== undefined) {
desc.value = _clone(obj[prop]);
}
Object.defineProperty(clone, prop, desc);
}
return clone;
};
return _clone;
};
// codebeat:enable[ABC,BLOCK_NESTING]
/**
* Deep clone of enumerable properties.
*
* @param {object} obj Any object
* @returns {object} The clone.
* @private
*/
var looseClone = cloneFac(getKeysFac(false));
/**
* Deep clone of all the properties, including the non-enumerable ones.
*
* @param {object} obj Any object or value
* @returns {object} The clone.
* @private
*/
var 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
*/
var deepClone = function _deepClone(value, exact) {
return exact ? exactClone(value) : looseClone(value);
};
module.exports = deepClone;
//# sourceMappingURL=index.js.map