123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- 'use strict';
- const Types = require('./types');
- const Utils = require('./utils');
- const internals = {
- needsProtoHack: new Set([Types.set, Types.map, Types.weakSet, Types.weakMap])
- };
- module.exports = internals.clone = function (obj, options = {}, _seen = null) {
- if (typeof obj !== 'object' ||
- obj === null) {
- return obj;
- }
- let clone = internals.clone;
- let seen = _seen;
- if (options.shallow) {
- if (options.shallow !== true) {
- return internals.cloneWithShallow(obj, options);
- }
- clone = (value) => value;
- }
- else {
- seen = seen || new Map();
- const lookup = seen.get(obj);
- if (lookup) {
- return lookup;
- }
- }
- // Built-in object types
- const baseProto = Types.getInternalProto(obj);
- if (baseProto === Types.buffer) {
- return Buffer && Buffer.from(obj); // $lab:coverage:ignore$
- }
- if (baseProto === Types.date) {
- return new Date(obj.getTime());
- }
- if (baseProto === Types.regex) {
- return new RegExp(obj);
- }
- // Generic objects
- const newObj = internals.base(obj, baseProto, options);
- if (newObj === obj) {
- return obj;
- }
- if (seen) {
- seen.set(obj, newObj); // Set seen, since obj could recurse
- }
- if (baseProto === Types.set) {
- for (const value of obj) {
- newObj.add(clone(value, options, seen));
- }
- }
- else if (baseProto === Types.map) {
- for (const [key, value] of obj) {
- newObj.set(key, clone(value, options, seen));
- }
- }
- const keys = Utils.keys(obj, options);
- for (const key of keys) {
- if (key === '__proto__') {
- continue;
- }
- if (baseProto === Types.array &&
- key === 'length') {
- newObj.length = obj.length;
- continue;
- }
- const descriptor = Object.getOwnPropertyDescriptor(obj, key);
- if (descriptor) {
- if (descriptor.get ||
- descriptor.set) {
- Object.defineProperty(newObj, key, descriptor);
- }
- else if (descriptor.enumerable) {
- newObj[key] = clone(obj[key], options, seen);
- }
- else {
- Object.defineProperty(newObj, key, { enumerable: false, writable: true, configurable: true, value: clone(obj[key], options, seen) });
- }
- }
- else {
- Object.defineProperty(newObj, key, {
- enumerable: true,
- writable: true,
- configurable: true,
- value: clone(obj[key], options, seen)
- });
- }
- }
- return newObj;
- };
- internals.cloneWithShallow = function (source, options) {
- const keys = options.shallow;
- options = Object.assign({}, options);
- options.shallow = false;
- const storage = Utils.store(source, keys); // Move shallow copy items to storage
- const copy = internals.clone(source, options); // Deep copy the rest
- Utils.restore(copy, source, storage); // Shallow copy the stored items and restore
- return copy;
- };
- internals.base = function (obj, baseProto, options) {
- if (baseProto === Types.array) {
- return [];
- }
- if (options.prototype === false) { // Defaults to true
- if (internals.needsProtoHack.has(baseProto)) {
- return new baseProto.constructor();
- }
- return {};
- }
- const proto = Object.getPrototypeOf(obj);
- if (proto &&
- proto.isImmutable) {
- return obj;
- }
- if (internals.needsProtoHack.has(baseProto)) {
- const newObj = new proto.constructor();
- if (proto !== baseProto) {
- Object.setPrototypeOf(newObj, proto);
- }
- return newObj;
- }
- return Object.create(proto);
- };
|