/**
* @module cloneDeep
* @description Performs a deep clone of the specified variable and returns the cloned variable.
*
* @since 3.1.0
*
* @param {*} value The value to clone.
*
* @return {*} Returns a deep copy of the specified variable.
*
* @example
* import { cloneDeep } from "@gravityforms/utils";
*
* function Example() {
* const obj = { prop: 'val' };
* const clone = cloneDeep( obj );
* }
*
*/
const cloneDeep = ( value ) => {
return _cloneDeepSafe( value );
};
/**
* @function _cloneDeepSafe
* @description Performs a deep clone of the specified variable and returns the cloned variable.
*
* @since 3.1.0
*
* @param {*} value The value to clone.
* @param {WeakMap} seen A WeakMap to handle circular references.
*
* @return {*} Returns a deep copy of the specified variable.
*
*/
const _cloneDeepSafe = ( value, seen = new WeakMap() ) => {
// Handle null, undefined, or primitive types
if ( value === null || typeof value !== 'object' ) {
return value;
}
// Handle circular references
if ( seen.has( value ) ) {
return seen.get( value );
}
// Handle Date objects
if ( value instanceof Date ) {
return new Date( value );
}
// Handle Arrays
if ( Array.isArray( value ) ) {
const copy = [];
seen.set( value, copy );
for ( let i = 0; i < value.length; i++ ) {
copy[ i ] = _cloneDeepSafe( value[ i ], seen );
}
return copy;
}
// Handle Map
if ( value instanceof Map ) {
const copy = new Map();
seen.set( value, copy );
value.forEach( ( v, k ) => {
copy.set( k, _cloneDeepSafe( v, seen ) );
} );
return copy;
}
// Handle Set
if ( value instanceof Set ) {
const copy = new Set();
seen.set( value, copy );
value.forEach( ( v ) => {
copy.add( _cloneDeepSafe( v, seen ) );
} );
return copy;
}
// Handle RegExp
if ( value instanceof RegExp ) {
return new RegExp( value );
}
// Handle Typed Arrays
if ( ArrayBuffer.isView( value ) ) {
return new value.constructor( value.buffer.slice( 0 ) );
}
// Handle plain objects and prototypes
if ( value instanceof Object ) {
const copy = Object.create( Object.getPrototypeOf( value ) );
seen.set( value, copy );
const keys = Reflect.ownKeys( value );
for ( const key of keys ) {
copy[ key ] = _cloneDeepSafe( value[ key ], seen );
}
return copy;
}
// If the value is an unsupported type, return it as is (functions, etc.)
return value;
};
export default cloneDeep;