A deep cloning algorithm aware of prototypes, getters/setters, etc.
npm install true-cloneThe goal of this package is to get as close as possible to a perfect JS value clone.
``js`
npm i true-clone
then
`js`
const { clone } = require('true-clone');
// later ...
const cloned = clone(myObject);
The cloning algorithm is pretty smart and is aware of:
- Native JS types! This includes primitives, Array, Set, Map, boxed primitives, typed arrays, etc.const ar = []; ar.my = 'prop'; console.assert(clone(ar).my === 'prop')
- 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: .
- (Non-)enumerability, (non-)configurability, and/or (non-)writability of object properties! These will be respected.
- etc.
- 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)Proxy
- objects: 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 cloneFunction
- Due to JS limitations, objects of the type , WeakSet, and WeakMap will not be cloned and will instead be returned as-is.
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 | ![s] | ![s] | ![s] | ![s] | ![s] | ![s] |clone
| [][1] 2.1.2 | ![s] | ![p] 1 | ![s] | ![p] 1 | ![s] | ![u] |lodash.clonedeep
| [][2] 4.5.0 | ![s] | ![p] 2 | ![s] | ![p] 3 | ![p] 4 | ![u] |rfdc
| [][3] 1.1.4 | ![s] | ![p] 5 | ![u] | ![u] | ![p] 6 | ![u] |
[s]: https://via.placeholder.com/15/0d0?text=+
[u]: https://via.placeholder.com/15/d00?text=+
[p]: https://via.placeholder.com/15/fc1?text=+
[1]: https://github.com/pvorb/clone
[2]: https://www.npmjs.com/package/lodash.clonedeep
[3]: https://github.com/davidmarkclements/rfdc#readme
Key
![s]: all tests passing; ![u]: no tests passing; ![p]: some tests passing
- primitives: supports primitive values
- native types: supports certain native types such as Array and Setconst ar = []; ar.my = 'prop'; console.assert(clone(ar).my === 'prop')
- prototypes: supports objects with prototypes
- monkeypatching: copies over monkeypatched attributes
- e.g. const ar = []; ar.push(ar);
- relations: preserves relational identity, such as in cyclic and diamond-shaped structures
- cyclic e.g. e.g. const child = { i_am: 'child' }; const parent = { child_a: child, child_b: child };
- diamonds e.g.
- rich properties: getters and setters etc.
Details
- 1: fails for Number, String, ArrayBuffer, DataView, errors types, and typed arrays.2
- : fails for sparse arrays, BigInt64Array, BigUint64Array, and error types3
- : fails for Array, BigInt64Array, BigUint64Array, and error types4
- : fails for cyclic Map and Set objects5
- : fails for Number, String, Boolean, RegExp, Map, Set, ArrayBuffer, DataView, typed arrays, and error types.6
- : fails for diamond shapes and cyclic non-Object values
true-clone pays for its correctness with speed.benchmark.js
Benchmark is run on my personal machine; they should be considered only in relation to each other.
See .
| package \ scope | primitives | native object types | plain objects | arrays |
| ----------------------------- | -------------- | ------------------- | ------------- | ------ |
| true-clone 1.0.0 | 2.300m [ops/s] | 343k | 440k | 1.219m |clone
| [][1] 2.1.2 | 1.823m | 96k | 261k | 263k |lodash.clonedeep
| [][2] 4.5.0 | 5.791m | 219k | 734k | 1.988m |rfdc
| [][3] 1.1.4 | 32.823m | 964k | 2.420m | 2.346m |
Details
- primitives: primitive objects; test case primitiveArray
- native object types: , Map, Set, and Boolean; test case obj typesObject :: plain small
- plain objects: JSON-able object; test case Array :: pure hom dense_ small`
- arrays: small, dense, non-monkeypatched arrays of primitive values; test case