From fb52c5aa27b0d4580d7c721271ad631cf3ba3f31 Mon Sep 17 00:00:00 2001 From: Tony Quetano Date: Mon, 26 Sep 2022 10:33:02 -0400 Subject: [PATCH] pass prototype through for clean clones, for faster object copying --- __tests__/utils.ts | 42 +++++++++++++++++++++++++++++++----------- src/index.ts | 6 +++--- src/utils.ts | 13 +++++++------ 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/__tests__/utils.ts b/__tests__/utils.ts index 2b7be8c..553823e 100644 --- a/__tests__/utils.ts +++ b/__tests__/utils.ts @@ -57,7 +57,7 @@ describe('getCleanClone', () => { it('will return a pure object when there is no constructor', () => { const object = Object.create(null); - const result = utils.getCleanClone(object); + const result = utils.getCleanClone(object, Object.getPrototypeOf(object)); expect(result).not.toBe(object); expect(result).toEqual(object); @@ -70,7 +70,7 @@ describe('getCleanClone', () => { object.__proto__ = null; - const result = utils.getCleanClone(object); + const result = utils.getCleanClone(object, Object.getPrototypeOf(object)); expect(result).not.toBe(object); expect(result).toEqual(object); @@ -81,7 +81,7 @@ describe('getCleanClone', () => { it('will return an empty POJO when the object passed is a POJO', () => { const object = { foo: 'bar' }; - const result = utils.getCleanClone(object); + const result = utils.getCleanClone(object, Object.getPrototypeOf(object)); expect(result).not.toBe(object); expect(result).toEqual({}); @@ -89,14 +89,14 @@ describe('getCleanClone', () => { expect(Object.getPrototypeOf(result)).toBe(Object.prototype); }); - it('will return an empty object with custom protype when the object created through Object.create()', () => { + it('will return an empty object with custom prototype when the object created through Object.create()', () => { const object = Object.create({ method() {}, }); object.foo = 'bar'; - const result = utils.getCleanClone(object); + const result = utils.getCleanClone(object, Object.getPrototypeOf(object)); expect(result).not.toBe(object); expect(result).toEqual({}); @@ -107,7 +107,7 @@ describe('getCleanClone', () => { it('will return an empty object with the given constructor when it is a global constructor', () => { const object = new Map(); - const result = utils.getCleanClone(object); + const result = utils.getCleanClone(object, Object.getPrototypeOf(object)); expect(result).not.toBe(object); expect(result).toEqual(new Map()); @@ -128,7 +128,7 @@ describe('getCleanClone', () => { const object = new Foo('bar'); - const result = utils.getCleanClone(object); + const result = utils.getCleanClone(object, Object.getPrototypeOf(object)); expect(result).not.toBe(object); expect(result).toEqual(Object.create(Foo.prototype)); @@ -155,7 +155,12 @@ describe('getObjectCloneLoose', () => { const handleCopy = jest.fn().mockImplementation((arg) => arg); const cache = utils.createCache(); - const result = utils.getObjectCloneLoose(object, handleCopy, cache); + const result = utils.getObjectCloneLoose( + object, + Object.getPrototypeOf(object), + handleCopy, + cache + ); Object.getOwnPropertySymbols = original; @@ -179,7 +184,12 @@ describe('getObjectCloneLoose', () => { const handleCopy = jest.fn().mockImplementation((arg) => arg); const cache = utils.createCache(); - const result = utils.getObjectCloneLoose(object, handleCopy, cache); + const result = utils.getObjectCloneLoose( + object, + Object.getPrototypeOf(object), + handleCopy, + cache + ); expect(result).not.toBe(object); expect(result).toEqual(object); @@ -215,7 +225,12 @@ describe('getObjectCloneStrict', () => { const handleCopy = jest.fn().mockImplementation((arg) => arg); const cache = utils.createCache(); - const result = utils.getObjectCloneStrict(object, handleCopy, cache); + const result = utils.getObjectCloneStrict( + object, + Object.getPrototypeOf(object), + handleCopy, + cache + ); Object.getOwnPropertySymbols = original; @@ -253,7 +268,12 @@ describe('getObjectCloneStrict', () => { const handleCopy = jest.fn().mockImplementation((arg) => arg); const cache = utils.createCache(); - const result = utils.getObjectCloneStrict(object, handleCopy, cache); + const result = utils.getObjectCloneStrict( + object, + Object.getPrototypeOf(object), + handleCopy, + cache + ); expect(result).not.toBe(object); expect(result).toEqual(object); diff --git a/src/index.ts b/src/index.ts index df3b151..5d515ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,12 +54,12 @@ function performCopy( // plain objects if (!Constructor || Constructor === Object) { - return getObjectClone(value, handleCopy, cache); + return getObjectClone(value, prototype, handleCopy, cache); } // arrays if (isArray(value)) { - return getArrayClone(value, handleCopy, cache); + return getArrayClone(value, prototype, handleCopy, cache); } const objectClass = toString.call(value); @@ -133,7 +133,7 @@ function performCopy( } // assume anything left is a custom constructor - return getObjectClone(value, handleCopy, cache); + return getObjectClone(value, prototype, handleCopy, cache); } return handleCopy(value, createCache()); diff --git a/src/utils.ts b/src/utils.ts index dd83478..f2b52bb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -55,10 +55,11 @@ export const createCache = export function getArrayCloneLoose( array: any[], + prototype: any, handleCopy: InternalCopier, cache: Cache ) { - const clone = new (array.constructor as any)(); + const clone = new prototype.constructor(); cache.set(array, clone); @@ -72,9 +73,7 @@ export function getArrayCloneLoose( /** * Get an empty version of the object with the same prototype it has. */ -export function getCleanClone(object: any): any { - const prototype = object.__proto__ || getPrototypeOf(object); - +export function getCleanClone(object: any, prototype: any): any { if (!prototype) { return create(null); } @@ -100,10 +99,11 @@ export function getCleanClone(object: any): any { */ export function getObjectCloneLoose( object: any, + prototype: any, handleCopy: InternalCopier, cache: Cache ): any { - const clone: any = getCleanClone(object); + const clone: any = getCleanClone(object, prototype); // set in the cache immediately to be able to reuse the object recursively cache.set(object, clone); @@ -149,10 +149,11 @@ const getStrictProperties = SYMBOL_PROPERTIES */ export function getObjectCloneStrict( object: any, + prototype: any, handleCopy: InternalCopier, cache: Cache ): any { - const clone: any = getCleanClone(object); + const clone: any = getCleanClone(object, prototype); // set in the cache immediately to be able to reuse the object recursively cache.set(object, clone);