본문으로 건너뛰기

clone

Creates a deep clone of any value with comprehensive type support and circular reference handling. Performs sophisticated deep cloning that handles primitives, objects, arrays, built-in types (Date, RegExp, Map, Set), TypedArrays, and complex objects while maintaining prototype chains and preventing infinite loops through circular reference detection.

Signature

const clone: <Type>(target: Type, maxDepth?: number) => Type

Parameters

NameTypeDescription
target-The value to create a deep clone of
maxDepth-Optional maximum depth to clone (objects beyond this depth are returned as references)

Returns

A new deep clone with identical structure and values

Examples

Primitive and basic object cloning

import { clone } from '@winglet/common-utils';

// Primitives (returned as-is)
console.log(clone(42)); // 42
console.log(clone('hello')); // 'hello'
console.log(clone(true)); // true

// Objects and arrays
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const cloned = clone(original);
cloned.b.c = 999;
console.log(original.b.c); // 2 (unchanged)
console.log(cloned.b.c); // 999

Built-in type cloning

// Date objects
const originalDate = new Date('2023-01-01');
const clonedDate = clone(originalDate);
clonedDate.setFullYear(2024);
console.log(originalDate.getFullYear()); // 2023
console.log(clonedDate.getFullYear()); // 2024

// RegExp objects
const originalRegex = /test/gi;
originalRegex.lastIndex = 5;
const clonedRegex = clone(originalRegex);
console.log(clonedRegex.source); // 'test'
console.log(clonedRegex.flags); // 'gi'
console.log(clonedRegex.lastIndex); // 5

// Map and Set
const originalMap = new Map([['key1', 'value1'], ['key2', { nested: true }]]);
const clonedMap = clone(originalMap);
clonedMap.get('key2').nested = false;
console.log(originalMap.get('key2').nested); // true (unchanged)

Circular reference handling

const obj: any = { name: 'parent' };
obj.self = obj; // Create circular reference
obj.child = { parent: obj }; // Another circular reference

const cloned = clone(obj);
console.log(cloned.name); // 'parent'
console.log(cloned.self === cloned); // true (maintains circular structure)
console.log(cloned.child.parent === cloned); // true
console.log(cloned !== obj); // true (different objects)

TypedArray and Buffer cloning

// TypedArrays
const uint8Array = new Uint8Array([1, 2, 3, 4]);
const clonedArray = clone(uint8Array);
clonedArray[0] = 99;
console.log(uint8Array[0]); // 1 (unchanged)
console.log(clonedArray[0]); // 99

// ArrayBuffers
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt32(0, 42);
const clonedBuffer = clone(buffer);
const clonedView = new DataView(clonedBuffer);
console.log(clonedView.getInt32(0)); // 42

Using maxDepth to limit cloning depth

const deep = {
level1: {
level2: {
level3: {
level4: {
data: 'very deep',
largeArray: new Array(10000).fill(0)
}
}
}
}
};

// Clone only 2 levels deep (improves performance for deep structures)
const shallowClone = clone(deep, 2);
console.log(shallowClone.level1 !== deep.level1); // true (cloned)
console.log(shallowClone.level1.level2 !== deep.level1.level2); // true (cloned)
console.log(shallowClone.level1.level2.level3 === deep.level1.level2.level3); // true (reference)

// Changes to deep levels don't affect the original
shallowClone.level1.level2.level3 = { modified: true };
console.log(deep.level1.level2.level3.level4.data); // 'very deep' (unchanged)

Complex object with custom properties

const complex = {
data: [1, 2, { nested: new Set([1, 2, 3]) }],
timestamp: new Date(),
pattern: /\w+/g,
metadata: new Map([['version', '1.0'], ['author', 'dev']])
};

// Add symbol property
const symbolKey = Symbol('secret');
complex[symbolKey] = 'hidden value';

const cloned = clone(complex);
console.log(cloned[symbolKey]); // 'hidden value'
console.log(cloned.data[2].nested.has(2)); // true
console.log(cloned !== complex); // true (different reference)

Playground

import { clone } from '@winglet/common-utils';

// Primitives (returned as-is)
console.log(clone(42)); // 42
console.log(clone('hello')); // 'hello'
console.log(clone(true)); // true

// Objects and arrays
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const cloned = clone(original);
cloned.b.c = 999;
console.log(original.b.c); // 2 (unchanged)
console.log(cloned.b.c); // 999

Notes

Supported Types:

  • Primitives: string, number, boolean, null, undefined, symbol, bigint
  • Objects: Plain objects, arrays, functions (as-is)
  • Built-ins: Date, RegExp, Map, Set, Error
  • Binary Data: ArrayBuffer, SharedArrayBuffer, TypedArrays, DataView
  • Web APIs: File, Blob (when available)
  • Custom Objects: Objects with custom prototypes

Key Features:

  • Circular Reference Safe: Detects and handles circular references
  • Prototype Preservation: Maintains original prototype chains
  • Symbol Support: Clones symbol properties
  • Property Descriptors: Respects writable property constraints
  • Performance Optimized: Uses efficient algorithms and caching

Internal Mechanisms:

  • Circular Reference Detection: Uses Map-based caching to track visited objects
  • Property Replication: replicateProperties handles both string keys and symbol properties
  • Descriptor Respect: Checks writable property descriptors before assignment
  • Prototype Chain: Uses Object.create(Object.getPrototypeOf(value)) to maintain inheritance
  • Type-Specific Cloning: Specialized handlers for Date, RegExp, Map, Set, TypedArrays

Performance Considerations:

  • Time Complexity: O(n) where n is total number of properties/elements
  • Space Complexity: O(d + c) where d is depth, c is circular references
  • Memory Usage: ~2x original object size during cloning process
  • Cache Efficiency: Map-based tracking for visited objects (automatic GC cleanup)
  • Optimization: Fast path for primitives, specialized handlers for built-in types

Browser/Runtime Compatibility:

  • ES2015+: Requires Symbol, Map, WeakMap support
  • Node.js: v6.0+ (full Symbol support)
  • Browsers: Chrome 49+, Firefox 36+, Safari 10+, Edge 12+
  • TypedArrays: IE10+ for basic support, modern browsers for full compatibility
  • File/Blob APIs: Browser environment only (gracefully handled in Node.js)

Performance Benchmarks (Node.js v18, typical hardware):

  • Small objects (< 100 props): ~0.1ms
  • Medium objects (< 1000 props): ~2ms
  • Large objects (< 10000 props): ~25ms
  • With circular references: +15% overhead
  • vs JSON.parse(JSON.stringify): ~3x slower but handles more types
  • vs Lodash cloneDeep: ~20% faster with better type support

Common Pitfalls:

  • Functions: Cloned by reference (not implementation)
  • DOM Elements: Not cloneable, returned as-is
  • Proxy Objects: May not clone as expected depending on handler implementation
  • Memory: Large objects can cause significant memory spike during cloning
  • WeakMap/WeakSet: Not cloneable (no way to iterate keys)

Production Considerations:

  • Memory: Monitor heap usage when cloning large objects
  • Performance: Consider caching for frequently cloned objects
  • Security: Be cautious with user-provided objects (prototype pollution)
  • Testing: Verify behavior with your specific object types