1.0.0 • Published 5 years ago
true-clone v1.0.0
js-true-clone
The goal of this package is to get as close as possible to a perfect JS value clone.
Usage
npm i true-clonethen
const { clone } = require('true-clone');
// later ...
const cloned = clone(myObject);Behaviour
The cloning algorithm is pretty smart and is aware of:
- Native JS types! This includes primitives,
Array,Set,Map, boxed primitives, typed arrays, etc. - Prototypes! Finally, you can clone custom classes!
- Getters! These will be replicated on the result as getters, not as the computed value.
- Setters! These will be replicated on the result.
- Custom properties on native types! For instance:
const ar = []; ar.my = 'prop'; console.assert(clone(ar).my === 'prop'). - (Non-)enumerability, (non-)configurability, and/or (non-)writability of object properties! These will be respected.
- etc.
Details
- Mostly works as one would expect!
- However, the following may be notable:
- Prototypes: are referenced rather than copied;
Object.is(clone(Object.create(someProto)).prototype, someProto) Proxyobjects: do not return other proxies. Additonally, all traps are ignored besides the following:getPrototypeOf: given prototype is assigned to new objectownKeys: these are the keys that will appear on the clonegetOwnPropertyDescriptor: is used to define properties on the clone
- Due to JS limitations, objects of the type
Function,WeakSet, andWeakMapwill not be cloned and will instead be returned as-is.
Comparison
Suite in tests.js run on different packages using node v14.2.0. See compare.sh.
| package \ feature | primitives | native types | prototypes | monkeypatching | relations | rich properites |
|---|---|---|---|---|---|---|
true-clone 1.0.0 | ||||||
clone 2.1.2 | ||||||
lodash.clonedeep 4.5.0 | ||||||
rfdc 1.1.4 |
: all tests passing;
: no tests passing;
: some tests passing
- primitives: supports primitive values
- native types: supports certain native types such as
ArrayandSet - prototypes: supports objects with prototypes
- monkeypatching: copies over monkeypatched attributes
- e.g.
const ar = []; ar.my = 'prop'; console.assert(clone(ar).my === 'prop')
- e.g.
- relations: preserves relational identity, such as in cyclic and diamond-shaped structures
- cyclic e.g. e.g.
const ar = []; ar.push(ar); - diamonds e.g.
const child = { i_am: 'child' }; const parent = { child_a: child, child_b: child };
- cyclic e.g. e.g.
- rich properties: getters and setters etc.
1: fails forNumber,String,ArrayBuffer,DataView, errors types, and typed arrays.2: fails for sparse arrays,BigInt64Array,BigUint64Array, and error types3: fails forArray,BigInt64Array,BigUint64Array, and error types4: fails for cyclicMapandSetobjects5: fails forNumber,String,Boolean,RegExp,Map,Set,ArrayBuffer,DataView, typed arrays, and error types.6: fails for diamond shapes and cyclic non-Objectvalues
Benchmarks
true-clone pays for its correctness with speed.
Benchmark is run on my personal machine; they should be considered only in relation to each other.
See benchmark.js.
| package \ scope | primitives | native object types | plain objects | arrays |
|---|---|---|---|---|
true-clone 1.0.0 | 2.300m ops/s | 343k | 440k | 1.219m |
clone 2.1.2 | 1.823m | 96k | 261k | 263k |
lodash.clonedeep 4.5.0 | 5.791m | 219k | 734k | 1.988m |
rfdc 1.1.4 | 32.823m | 964k | 2.420m | 2.346m |
- primitives: primitive objects; test case
primitive - native object types:
Array,Map,Set, andBoolean; test caseobj types - plain objects: JSON-able object; test case
Object :: plain small - arrays: small, dense, non-monkeypatched arrays of primitive values; test case
Array :: pure hom dense_ small