@golden-ants/clone
A new approach to deep cloning. A simple library with no external dependencies gives you the opportunity to customize the cloning strategy.
Table of Contents
Installing
Using npm:
npm install --save @golden-ants/clone
Using yarn:
yarn add @golden-ants/clone
Usage
You need to create a switcher tree that simulates copying the contents of each object property.
const switchers: Switcher[] = [
{
if: not(isObject), // all primitive types
handler: (val) => val, // copying by value
},
{
if: isObject,
then: [
{
// copying functions, but not constructor functions
if: createdBy(Function), // (val) => val.constructor === Function
handler: (val) => val, // copying by reference
},
{
if: createdBy(Date), // (val) => val.constructor === Date
handler: (val) => new Date(val as Date),
},
{
if: instanceOf(Array), // (val) => val instanceof Array
/** Copy the (value) to a new empty array by
* calling the cloning method with the same
* switcher scheme.*/
handler: retryFor(() => Array.of()),
},
{
/** For JavaScript objects created using standard methods.
* We do not use "instanceOf(Object)" because we are not
* sure that there will be no more instances of specific
* classes in the input stream.
*/
if: createdBy(Object),
handler: retryFor(() => Object.create({})),
},
{
/** For JavaScript objects created without a parent prototype.*/
if: and(isObject, createdBy(undefined)),
handler: retryFor(() => Object.create(null)),
},
//{
// /**We repeat the parent signature of the predicate in case
// * none of the conditions are met to throw an exception. Another way is for the
// * other objects to copy the value by reference. */
// if: isObject,
// handler: (value) => {
// /**This is the default behavior, if no condition is met,
// * an exception will be thrown. */
// //throw new SwitcherNotFoundException(value) // no need to write this
// // or
// //return value
// },
//},
],
},
]
export const deepClone = new Clone(switchers)
Calling the created deepClone
for deep copying of objects or arrays.
Note: The target of copying can be an
array
or anobject
, but array elements or object property keys can only be those types that meet the conditions of theswitchers
.
import { deepClone } from ...
// object (array) of any nesting level
const foo = {
nil: null,
indefinite: undefined,
foo() { return 'bar' },
array: [
{ date: new Date(), storage: Object.create(null) },
{ date: new Date(), storage: Object.create(null) },
{ date: new Date(), storage: Object.create(null) },
],
}
const copied = deepClone.it({}, foo);
Switcher
Switchers are necessary for the unique processing of an object's property.
Each switcher contains an if
property that accepts a predicate function, the second property can be either a handler or an array of nested switchers.
{
if: [Predicate: (value) => boolean],
handler: [Handler: (value, clone) => unknown)
}
or
{
if: [Predicate: (value) => boolean],
then: [Switchers]
}
Warning: If you define the
then
andhandler
properties in an Switcher, the consequences will be unpredictable
Clone
An instance of the Clone
class contains a single public member - the it
method.
Cloning occurs superficially, a handler
is applied to each property of the source
, the result of which is assigned to the target
. But thanks to the hook, you can call the cloning again, but for a nested object.
const clone = new Clone(...)
const target = {}
const source = { foo: "bar" }
const copied = clone.it(target, source)
copied === target // true
copied === source // false
And so the handler
can cause cloning for a nested object.
{
if: isObject,
handler: (value, clone) => clone.it({}, value),
// handler: retryFor(() => ({}))
}
Functional utilities
Predicates
function createdBy(constructor) {}
function instanceOf(type) {}
// also: isObject, isRecord, isNull, isUndefined, isNumber, isString, isBoolean, isSymbol
Compose
function and(...fs: Predicates): Predicate {}
function or(...fs: Predicates): Predicate {}
function not(predicate: Predicate): Predicate {}
function xor(first: Predicate, second: Predicate): Predicate {}
Handlers
function retryFor(producer: () => object): Handler {}