Package Exports
- @visulima/deep-clone
- @visulima/deep-clone/handler
- @visulima/deep-clone/package.json
- @visulima/deep-clone/utils
Readme
Fastest deep clone implementation.
Daniel Bannert's open source work is supported by the community on GitHub Sponsors
Install
npm install @visulima/deep-cloneyarn add @visulima/deep-clonepnpm add @visulima/deep-cloneUsage
Copy or deep clone an input value to an arbitrary depth. The function accepts both objects and primitives.
import { deepClone } from "@visulima/deep-clone";
const cloned = deepClone({ a: 1, b: { c: 2 } });
console.log(cloned); // => {a: 1, b: {c: 2}}createDeepClone(options?)
For hot loops that clone many values with the same configuration, create a pre-configured cloner once and reuse it. This resolves the handler table a single time instead of on every call.
import { createDeepClone } from "@visulima/deep-clone";
const cloneStrict = createDeepClone({ strict: true });
const a = cloneStrict({ a: 1 });
const b = cloneStrict({ b: 2 });API
deepClone(input, options?)
input
Type: any
The input value to copy.
options
Type: object
strict
Type: boolean
Default: false
If true, it will copy all properties, including non-enumerable ones and symbols.
handler
Type: object (all keys optional)
A set of custom handlers for specific types of value. Each handler is a function (value, state) => value that takes the original value and returns a new value (or throws if the value is not supported). You only need to provide the keys you want to override — the rest fall back to the built-in defaults:
import { deepClone } from "@visulima/deep-clone";
// Override just the Date handler; everything else uses the defaults.
const cloned = deepClone(
{ when: new Date() },
{
handler: {
Date: (date) => new Date(date.getTime() + 1000),
},
},
);Supported handler keys (each is an InternalHandler<T>):
ArrayArrayBufferBlobDataViewDateErrorFileFunctionMapObjectPromiseRegExpSetSharedArrayBufferWeakMapWeakSet
Note: typed arrays (
Uint8Array,Float64Array, …) are always cloned through theArrayBufferhandler, so there are no dedicated typed-array keys.
Utils
copyOwnProperties(input)
Copy all properties contained on the object.
import { copyOwnProperties } from "@visulima/deep-clone/utils";
const obj = { a: 1, b: 2 };
const copy = copyOwnProperties(obj);
console.log(copy); // => {a: 1, b: 2}getCleanClone(input)
Get an empty version of the object with the same prototype it has.
import { getCleanClone } from "@visulima/deep-clone/utils";
const obj = { a: 1, b: 2 };
const clean = getCleanClone(obj);
console.log(clean); // => {}Notes
List of supported values/types:
undefined(original value is returned)null(original value is returned)boolean/Boolean(original value is returned)string/String(original value is returned)number/Number(original value is returned)functionObjectDateRegExpSetMap- [
Error][mdn-error] - [
URIError][mdn-uri-error] - [
ReferenceError][mdn-reference-error] - [
SyntaxError][mdn-syntax-error] - [
RangeError][mdn-range-error] - [
EvalError][mdn-eval-error] - [
TypeError][mdn-type-error] - [
System Error][node-system-error] (Node.js) ArrayInt8ArrayUint8ArrayUint8ClampedArrayInit16ArrayUint16ArrayInt32ArrayUint32ArrayFloat32ArrayFloat64ArrayBuffer([Node.js][node-buffer])DataViewBlobFile(preservesnameandlastModified)
List of unsupported values/types:
DOMElement: to copy DOM elements, useelement.cloneNode().SymbolWeakMapWeakSetFileListImageDataImageBitmapPromiseSharedArrayBuffer
The implementation can handle circular references.
If a
Number,String, orBooleanobject is encountered, the value is cloned as a primitive. This behavior is intentional. The implementation is opinionated in wanting to avoid creatingnumbers,strings, andbooleansvia thenewoperator and a constructor.The implementation only checks whether basic
Objects,Arrays, and class instances areextensible,sealed, and/orfrozen.functionsare not cloned; their reference is copied.The implementation supports custom [
error][mdn-error] types which are [Error][mdn-error] instances (e.g., ES2015 subclasses).
Benchmarks
Note: Run
pnpm --filter "deep-clone-bench" run test:benchto run the benchmarks yourself.
Shallow Clone Performance
Performance comparison for shallow object cloning { a: "a", b: "b", c: "c" }:
| Library | Operations/sec | Relative Performance |
|---|---|---|
| plain-object-clone | 5,650,888 | 1.00x (fastest) |
| fast-copy | 5,056,607 | 0.89x |
| rfdc - default | 4,674,249 | 0.83x |
| deep-copy | 4,522,595 | 0.80x |
| rfdc - proto | 4,371,388 | 0.77x |
| nanoclone | 4,282,256 | 0.76x |
| @visulima/deep-clone - loose | 3,551,940 | 0.63x |
| clone-deep | 3,740,025 | 0.66x |
| nano-copy | 3,673,002 | 0.65x |
| rfdc - circles and proto | 3,423,507 | 0.61x |
| rfdc - circles | 3,235,513 | 0.57x |
| ramda.clone | 2,514,785 | 0.44x |
| lodash.clonedeep | 2,236,361 | 0.40x |
| structured-clone | 1,503,190 | 0.27x |
| @mfederczuk/deeptools | 1,305,034 | 0.23x |
| @ungap/structured-clone | 1,231,364 | 0.22x |
| fast-copy strict | 1,196,932 | 0.21x |
| @visulima/deep-clone - strict | 930,414 | 0.16x |
Deep Clone Performance
Performance comparison for deep object cloning (complex nested structure):
| Library | Operations/sec | Relative Performance |
|---|---|---|
| rfdc - proto | 412,630 | 1.00x (fastest) |
| rfdc - default | 400,110 | 0.97x |
| rfdc - circles | 369,861 | 0.90x |
| rfdc - circles and proto | 367,846 | 0.89x |
| nanoclone | 314,105 | 0.76x |
| deep-copy | 301,609 | 0.73x |
| plain-object-clone | 249,603 | 0.60x |
| nano-copy | 248,646 | 0.60x |
| fast-copy | 228,871 | 0.55x |
| clone-deep | 217,880 | 0.53x |
| @visulima/deep-clone - loose | 194,650 | 0.47x |
| lodash.clonedeep | 130,934 | 0.32x |
| structured-clone | 90,495 | 0.22x |
| @ungap/structured-clone | 88,710 | 0.21x |
| ramda.clone | 58,385 | 0.14x |
| fast-copy strict | 34,771 | 0.08x |
| @visulima/deep-clone - strict | 33,065 | 0.08x |
| @mfederczuk/deeptools | 15,357 | 0.04x |
Important Notes
- @visulima/deep-clone provides the best balance between performance and feature completeness
- Loose mode (default): Fast performance with standard cloning behavior
- Strict mode: Slower but clones all properties including non-enumerable ones and symbols
- Libraries like
plain-object-cloneandfastest-json-copyare faster but have significant limitations:- Cannot handle circular references
- Treat complex objects (Date, Map, etc.) as plain objects
- Limited type support
- rfdc is very fast but has fewer features than @visulima/deep-clone
Supported Node.js Versions
Libraries in this ecosystem make the best effort to track Node.js’ release schedule. Here’s a post on why we think this is important.
Contributing
If you would like to help take a look at the list of issues and check our Contributing guild.
Note: please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
Credits
Made with ❤️ at Anolilab
This is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. Anolilab is a Development and AI Studio. Contact us at hello@anolilab.com if you need any help with these technologies or just want to say hi!
License
The visulima deep-clone is open-sourced software licensed under the MIT