A comprehensive deep cloning utility for JavaScript that handles primitive types, complex objects, and cyclic references with ease.
- 🔄 Deep cloning with cyclic reference handling
- 🏗️ Preserves prototypes and object descriptors
- 🎯 Type-safe with TypeScript support
- 📦 Lightweight with zero dependencies
- 🚀 Comprehensive support for all JavaScript types
npm install @ibnlanre/clone
import clone from "@ibnlanre/clone";
const original = { name: "John", age: 30 };
const cloned = clone(original);
const clone = require("@ibnlanre/clone");
const original = { name: "John", age: 30 };
const cloned = clone(original);
<script src="https://unpkg.com/@ibnlanre/clone"></script>
<script>
const cloned = clone({ name: "John", age: 30 });
</script>
All primitive types are handled correctly:
clone(undefined); // → undefined
clone(null); // → null
clone(true); // → true
clone(42); // → 42
clone("hello"); // → "hello"
clone(BigInt(123)); // → 123n
clone(Symbol("id")); // → Symbol(id)
// Plain objects
const obj = { a: 1, b: { c: 2 } };
const clonedObj = clone(obj);
// Arrays
const arr = [1, [2, 3], { d: 4 }];
const clonedArr = clone(arr);
// Nested structures
const complex = {
users: [
{ id: 1, profile: { name: "Alice" } },
{ id: 2, profile: { name: "Bob" } },
],
};
const clonedComplex = clone(complex);
Functions are cloned with all their properties preserved:
function greet(name) {
return `Hello, ${name}!`;
}
greet.customProp = "custom value";
greet.prototype.sayGoodbye = () => "Goodbye!";
const clonedGreet = clone(greet);
clonedGreet("World"); // → "Hello, World!"
clonedGreet.customProp; // → "custom value"
clonedGreet.prototype.sayGoodbye(); // → "Goodbye!"
clonedGreet !== greet; // → true (different reference)
const date = new Date("2023-12-25");
const clonedDate = clone(date);
// → 2023-12-25T00:00:00.000Z
const regex = /hello/gi;
const clonedRegex = clone(regex);
// → /hello/gi (with same flags)
const map = new Map([
["key1", "value1"],
["key2", { nested: "object" }],
]);
const clonedMap = clone(map);
// → Map with deeply cloned keys and values
const set = new Set([1, { a: 2 }, [3, 4]]);
const clonedSet = clone(set);
// → Set with deeply cloned values
ArrayBuffers and Typed Arrays
// ArrayBuffer
const buffer = new ArrayBuffer(16);
const clonedBuffer = clone(buffer);
// Typed Arrays
const int32Array = new Int32Array([1, 2, 3, 4]);
const clonedInt32Array = clone(int32Array);
// DataView
const dataView = new DataView(buffer, 4, 8);
const clonedDataView = clone(dataView);
const error = new Error("Something went wrong");
error.code = "E001";
error.details = { timestamp: Date.now() };
const clonedError = clone(error);
// → Error with message, stack, and custom properties cloned
const url = new URL("https://example.com/path?query=value");
const clonedUrl = clone(url);
const params = new URLSearchParams("a=1&b=2");
const clonedParams = clone(params);
Handles circular references without infinite loops:
const obj = { name: "parent" };
obj.child = { name: "child", parent: obj };
obj.self = obj;
const cloned = clone(obj);
// → Properly cloned with circular references preserved
cloned.child.parent === cloned; // → true
cloned.self === cloned; // → true
Object prototypes and property descriptors are preserved:
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
const person = new Person("Alice");
const clonedPerson = clone(person);
clonedPerson instanceof Person; // → true
clonedPerson.greet(); // → "Hello, I'm Alice"
clonedPerson !== person; // → true
clone<T>(value: T, visited?: WeakMap): T
Creates a deep clone of the provided value.
Parameters:
-
value - The value to clone
-
visited - (Optional) WeakMap for tracking circular references
Returns:
- A deep clone of the input value
Type Safety:
- Maintains TypeScript type information
- Returns the same type as the input
Creating Custom Clone Functions
The library provides powerful customization capabilities through createCloneFunction and the registry system. This allows you to handle custom types or modify existing behavior.
import { createCloneFunction } from "@ibnlanre/clone";
// Create a custom clone function
const customClone = createCloneFunction();
// Use it like the default clone
const cloned = customClone(originalObject);
Adding Custom Type Handlers
import { createCloneFunction, CloneRegistry } from "@ibnlanre/clone";
// Define a custom class
class MyCustomType {
constructor(data) {
this.data = data;
this.timestamp = Date.now();
}
}
// Create a custom clone function with registry modifier
const customClone = createCloneFunction((registry) => {
registry.setHandler(MyCustomType, (value, visited, clone) => {
// Custom cloning logic for MyCustomType
const result = new MyCustomType(clone(value.data, visited));
result.timestamp = value.timestamp; // Preserve original timestamp
visited.set(value, result);
return result;
});
});
// Now MyCustomType instances are cloned with custom logic
const original = new MyCustomType({ nested: { value: 42 } });
const cloned = customClone(original);
The CloneRegistry class provides methods to manage type handlers:
import { CloneRegistry, Handlers } from "@ibnlanre/clone";
const registry = new CloneRegistry();
// Check if a handler exists
if (registry.hasHandler(MyCustomType)) {
console.log("Handler exists");
}
// Set a custom handler
registry.setHandler(MyCustomType, Handlers.Identity); // Use identity handler
// Get a handler for a value
const handler = registry.getHandler(someValue);
The library exports pre-built handlers you can reuse:
import { createCloneFunction, Handlers } from "@ibnlanre/clone";
const customClone = createCloneFunction((registry) => {
// Use identity handler for custom types (no cloning)
registry.setHandler(MyImmutableType, Handlers.Identity);
// Use object handler for plain object-like types
registry.setHandler(MyPlainObjectType, Handlers.Object);
// Use array handler for array-like types
registry.setHandler(MyArrayLikeType, Handlers.Array);
});
Complex Custom Handler Example
import { createCloneFunction } from "@ibnlanre/clone";
class DatabaseModel {
constructor(id, data) {
this.id = id;
this.data = data;
this._metadata = { created: Date.now() };
}
}
const dbClone = createCloneFunction((registry) => {
registry.setHandler(DatabaseModel, (value, visited, clone) => {
// Create new instance with cloned data but preserve ID
const result = new DatabaseModel(
value.id, // Keep original ID
clone(value.data, visited) // Deep clone data
);
// Clone metadata separately
result._metadata = clone(value._metadata, visited);
visited.set(value, result);
return result;
});
});
// Usage
const original = new DatabaseModel("user_123", {
name: "Alice",
preferences: { theme: "dark" },
});
const cloned = dbClone(original);
// cloned.id === original.id (preserved)
// cloned.data !== original.data (deep cloned)
Handler Function Signature
Custom handlers receive three parameters:
type CloneHandler<T> = (
value: T, // The value to clone
visited: WeakMap<object, any>, // Circular reference tracker
clone: CloneFunction // The clone function for recursive cloning
) => T;
Important: Always call visited.set(value, result) before recursively cloning properties to prevent infinite loops with circular references.
You can override handlers for built-in types:
const customClone = createCloneFunction((registry) => {
// Custom Date handler that rounds to nearest second
registry.setHandler(Date, (value) => {
const rounded = new Date(Math.round(value.getTime() / 1000) * 1000);
return rounded;
});
// Custom Array handler that filters out null values
registry.setHandler(Array, (value, visited, clone) => {
const result = [];
visited.set(value, result);
for (let i = 0; i < value.length; i++) {
if (i in value && value[i] !== null) {
result[i] = clone(value[i], visited);
}
}
return result;
});
});
🏆 Exceptional Performance
This clone utility delivers world-class performance across all JavaScript data types:
-
Simple Objects: 2.57M operations/sec (0.0004ms per clone)
-
Circular References: 1.64M operations/sec (0.0006ms per clone)
-
Functions: 2.31M operations/sec (0.0004ms per clone)
-
Complex Objects: 25.4K operations/sec (0.0394ms per clone)
-
Comprehensive Data Types: 93.4K operations/sec (0.0107ms per clone)
When compared to JSON.parse(JSON.stringify()):
-
Clone: 10,680 ops/sec for complex objects
-
JSON method: 3,555 ops/sec for same objects
-
Result: 3x faster with full feature support!
Critical advantage: JSON method fails on functions, dates become strings, no circular references, loses prototypes, etc.
🎯 Production-Ready Features
-
Linear Scaling: Predictable O(n) performance across object sizes
-
Memory Efficient: Uses WeakMap for cycle detection without memory leaks
-
Comprehensive Types: Handles all JavaScript types including advanced ones
-
Robust Error Handling: Graceful handling of edge cases
Operation Type
Ops/Second
Avg Time (ms)
Use Case
| Simple Objects |
2,569,159 |
0.0004 |
Config, primitives |
| Circular Refs |
1,641,419 |
0.0006 |
Complex structures |
| Functions |
2,305,586 |
0.0004 |
Component cloning |
| Comprehensive |
93,373 |
0.0107 |
All data types |
| Complex Objects |
25,396 |
0.0394 |
Nested structures |
-
State Management: Perfect for Redux/Zustand/Recoil stores
-
API Response Cloning: Excellent for server data processing
-
Configuration Objects: Outstanding performance for app settings
-
Form Data Handling: Efficient user input cloning and validation
-
Caching Systems: Great for cache invalidation and snapshots
-
Real-time Applications: Suitable for high-frequency operations
See PERFORMANCE_REPORT.md for detailed benchmarks and analysis.
- Uses WeakMap for efficient circular reference tracking
- Minimizes object creation for primitive types
- Preserves prototype chain without unnecessary copying
- Optimized for common use cases with linear scaling
- Memory-safe implementation with automatic garbage collection
MIT © Ridwan Olanrewaju