UNPKG

@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
"use strict"; /// <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