bunshin-clone
v1.2.2
Published
High-performance deep clone utility with descriptor support
Maintainers
Readme
Bunshin Clone
High-performance deep clone utility with descriptor support. Handles circular ref and complex built-in types.
Install
npm i bunshin-clone// npm
import bunshinClone from 'bunshin-clone';
// CDNs
import bunshinClone from 'https://esm.sh/bunshin-clone'
// or
import bunshinClone from 'https://cdn.jsdelivr.net/npm/bunshin-clone/+esm';
// or
import bunshinClone from 'https://unpkg.com/bunshin-clone/dist/index.js';📦 APIs
bunshinClone(source, options);
// => T
//
// source: T
// options (optional): BunshinCloneOptions🪄 Options
interface BunshinCloneOptions {
preserveDescriptors?: boolean; // (default: false)
strictDescriptors?: boolean; // (default: false)
}preserveDescriptors
If true, preserves property descriptors (getters/setters, etc.).
strictDescriptors
If true, throws if descriptor cannot be merged (e.g. non-configurable or non-writable)
📖 Details
Example
const source = { foo: 1, nested: { x: 1 } };
const result = bunshinClone(source);
console.log(result);
// { foo: 1, nested: { x: 1 } }
console.log(result === source); // false
console.log(result.nested === source.nested); // falseSupported Types
bunshin-clone correctly handles:
- Object (plain + prototype preserved)
- Array
- Map
- Set
- Date
- RegExp
- ArrayBuffer
- DataView
- TypedArray (Uint8Array, etc.)
- Error / DOMException
- Blob
- ImageData
- URL
- URLSearchParams
Circular ref
const a: any = { x: 1 };
a.self = a;
const result = bunshinClone(a);
result.self === result; // trueDescriptor Behavior
Default (fast path)
const source = {
get x() {
return 42;
}
};
const result = bunshinClone(source);
result.x; // 42
// getter is NOT preservedpreserveDescriptors: true
const source = {};
Object.defineProperty(source, 'x', {
get: () => 42,
enumerable: true,
});
const result = bunshinClone(source, {
preserveDescriptors: true,
});
Object.getOwnPropertyDescriptor(result, 'x')?.get;
// => preservedUnsupported / Pass-through Types
Some values are returned as-is:
- Function
- WeakMap / WeakSet
- Proxy (not cloned)
- Other non-cloneable host objects
const fn = () => {};
bunshinClone(fn) === fn; // trueDesign Notes
Deep clone (no structural sharing)
Unlike merge utilities, bunshin-clone always produces a new structure:
const source = { a: { b: 1 } };
const result = bunshinClone(source);
result !== source; // true
result.a !== source.a; // trueGetter / Setter behavior
- Default: evaluated and converted to value
- preserveDescriptors: preserved as-is
Descriptor safety
When preserveDescriptors is enabled:
- descriptors are cloned safely
- original object is never mutated
- errors are controlled via
strictDescriptors
Performance
- No proxy / no diffing
- Minimal branching
- Fast path for plain objects and arrays
- Competitive with structuredClone in many cases
Comparison
| Feature | Bunshin Clone | structuredClone | lodash.clonedeep | |---------------------|--------------|----------------|------------------| | Circular refs | ✅ | ✅ | ✅ | | Map / Set | ✅ | ✅ | ⚠️ (partial) | | TypedArray | ✅ | ✅ | ⚠️ (shallow) | | Descriptor support | ✅ | ❌ | ❌ | | Functions | pass-through | ❌ (throws) | pass-through | | Prototype preserved | ✅ | ❌ | ⚠️ | | Custom control | ✅ | ❌ | ❌ | | Performance | ⚡ fast | ⚡ fast | 🐢 slower |
