Skip to content

Commit

Permalink
store object-specific copiers in closure
Browse files Browse the repository at this point in the history
  • Loading branch information
planttheidea committed Sep 29, 2022
1 parent 50e85d1 commit 7f8be45
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 71 deletions.
82 changes: 31 additions & 51 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ import {
copyRegExp,
copySet,
} from './copier';
import {
ARRAY_BUFFER_OBJECT_CLASSES,
UNCOPIABLE_OBJECT_CLASSES,
createCache,
} from './utils';
import { createCache, identity } from './utils';

import type { InternalCopier, State } from './copier';

Expand Down Expand Up @@ -51,6 +47,31 @@ export function createCopier(options: CreateCopierOptions) {
set = copySet,
} = options;

const typeSpecificCopiers: Record<string, InternalCopier> = {
'[object ArrayBuffer]': arrayBuffer,
'[object Blob]': blob,
'[object DataView]': dataView,
'[object Date]': date,
'[object Error]': identity,
'[object Float32Array]': arrayBuffer,
'[object Float64Array]': arrayBuffer,
'[object Int8Array]': arrayBuffer,
'[object Int16Array]': arrayBuffer,
'[object Int32Array]': arrayBuffer,
'[object Map]': map,
'[object Object]': object,
'[object Promise]': identity,
'[object RegExp]': regExp,
'[object Set]': set,
'[object WeakMap]': identity,
'[object WeakSet]': identity,
'[object Uint8Array]': arrayBuffer,
'[object Uint8ClampedArray]': arrayBuffer,
'[object Uint16Array]': arrayBuffer,
'[object Uint32Array]': arrayBuffer,
'[object Uint64Array]': arrayBuffer,
};

function copier(value: any, state: State): any {
state.prototype = state.Constructor = undefined;

Expand Down Expand Up @@ -79,55 +100,14 @@ export function createCopier(options: CreateCopierOptions) {
return array(value, state);
}

const objectClass = toString.call(value);

// dates
if (objectClass === '[object Date]') {
return date(value, state);
}

// regexps
if (objectClass === '[object RegExp]') {
return regExp(value, state);
}

// maps
if (objectClass === '[object Map]') {
return map(value, state);
}

// sets
if (objectClass === '[object Set]') {
return set(value, state);
}
const objectType = toString.call(value);
const typeSpecificCopier = typeSpecificCopiers[objectType];

// blobs
if (objectClass === '[object Blob]') {
return blob(value, state);
}

// dataviews
if (objectClass === '[object DataView]') {
return dataView(value, state);
}

// array buffers
if (ARRAY_BUFFER_OBJECT_CLASSES[objectClass]) {
return arrayBuffer(value, state);
}

// if the value cannot / should not be copied deeply, return the reference
if (
// promise-like
typeof value.then === 'function' ||
// object classes which cannot be introspected for copy
UNCOPIABLE_OBJECT_CLASSES[objectClass]
) {
return value;
if (typeSpecificCopier) {
return typeSpecificCopier(value, state);
}

// assume anything left is a custom constructor
return object(value, state);
return typeof value.then === 'function' ? value : object(value, state);
}

return function copy<Value>(value: Value): Value {
Expand Down
24 changes: 4 additions & 20 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,6 @@ export interface Cache {
const { toString: toStringFunction } = Function.prototype;
const { create } = Object;

export const ARRAY_BUFFER_OBJECT_CLASSES: Record<string, boolean> = {
['[object ArrayBuffer]']: true,
['[object Float32Array]']: true,
['[object Float64Array]']: true,
['[object Int8Array]']: true,
['[object Int16Array]']: true,
['[object Int32Array]']: true,
['[object Uint8Array]']: true,
['[object Uint8ClampedArray]']: true,
['[object Uint16Array]']: true,
['[object Uint32Array]']: true,
['[object Uint64Array]']: true,
};
export const UNCOPIABLE_OBJECT_CLASSES: Record<string, boolean> = {
['[object Error]']: true,
['[object Promise]']: true,
['[object WeakMap]']: true,
['[object WeakSet]']: true,
};

class LegacyCache {
_keys: any[] = [];
_values: any[] = [];
Expand Down Expand Up @@ -119,3 +99,7 @@ function getRegExpFlagsModern(regExp: RegExp): string {
*/
export const getRegExpFlags =
/test/g.flags === 'g' ? getRegExpFlagsModern : getRegExpFlagsLegacy;

export function identity<Value>(value: Value): Value {
return value;
}

0 comments on commit 7f8be45

Please sign in to comment.