Skip to content

Commit

Permalink
Improve clone() performance. Closes #344
Browse files Browse the repository at this point in the history
  • Loading branch information
hueniverse committed Oct 17, 2019
1 parent 8fa5664 commit f6eb28d
Showing 1 changed file with 67 additions and 52 deletions.
119 changes: 67 additions & 52 deletions lib/clone.js
Expand Up @@ -36,48 +36,26 @@ module.exports = internals.clone = function (obj, options = {}, _seen = null) {
}
}

// Built-in object types

const baseProto = Types.getInternalProto(obj);
let newObj;

switch (baseProto) {
case Types.buffer:
return Buffer && Buffer.from(obj); // $lab:coverage:ignore$

case Types.date:
return new Date(obj.getTime());

case Types.regex:
return new RegExp(obj);

case Types.array:
newObj = [];
break;

default:
if (options.prototype !== false) { // Defaults to true
const proto = Object.getPrototypeOf(obj);
if (proto &&
proto.isImmutable) {

return obj;
}

if (internals.needsProtoHack.has(baseProto)) {
newObj = new proto.constructor();
if (proto !== baseProto) {
Object.setPrototypeOf(newObj, proto);
}
}
else {
newObj = Object.create(proto);
}
}
else if (internals.needsProtoHack.has(baseProto)) {
newObj = new baseProto.constructor();
}
else {
newObj = {};
}
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) {
Expand All @@ -96,35 +74,38 @@ module.exports = internals.clone = function (obj, options = {}, _seen = null) {
}

const keys = Utils.keys(obj, options);
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];

for (const key of keys) {
if (baseProto === Types.array &&
key === 'length') {

newObj.length = obj.length;
continue;
}

const descriptor = Object.getOwnPropertyDescriptor(obj, key);
if (descriptor &&
(descriptor.get || descriptor.set)) {
if (descriptor) {
if (descriptor.get ||
descriptor.set) {

Object.defineProperty(newObj, key, descriptor);
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: descriptor ? descriptor.enumerable : true,
enumerable: true,
writable: true,
configurable: true,
value: clone(obj[key], options, seen)
});
}
}

if (baseProto === Types.array) {
newObj.length = obj.length;
}

return newObj;
};

Expand All @@ -140,3 +121,37 @@ internals.cloneWithShallow = function (source, options) {
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);
};

0 comments on commit f6eb28d

Please sign in to comment.