Hone logo
Hone
Problems

Deep Cloning Objects with Circular References in JavaScript

Deep cloning is the process of creating a completely independent copy of an object, including all nested objects and arrays. This is crucial when you need to modify a copy of an object without affecting the original. However, handling circular references (where an object references itself, directly or indirectly) during deep cloning can be tricky and requires a specialized approach to avoid infinite loops.

Problem Description

You are tasked with creating a function deepClone(obj) that performs a deep clone of any JavaScript object, including those containing circular references. The function should return a new object that is a completely independent copy of the input object. Modifications to the cloned object should not affect the original object, and vice versa. The function must correctly handle primitive data types (numbers, strings, booleans, null, undefined), arrays, and objects, including those with nested structures and circular references.

Key Requirements:

  • Deep Copy: The returned object must be a deep copy, meaning all nested objects and arrays are also copied, not just referenced.
  • Circular Reference Handling: The function must gracefully handle circular references without entering an infinite loop. It should maintain the circular references in the cloned object.
  • Primitive Types: Primitive data types (string, number, boolean, null, undefined) should be copied by value.
  • Arrays: Arrays should be cloned, with their elements also cloned recursively.
  • Objects: Objects should be cloned, with their properties also cloned recursively.
  • Functions: Functions should not be cloned. They should be omitted from the cloned object.

Expected Behavior:

The deepClone(obj) function should return a new object that is a deep copy of the input obj. The cloned object should have the same structure and values as the original, but it should be a completely separate object in memory.

Edge Cases to Consider:

  • Null or Undefined Input: The function should handle null and undefined inputs gracefully, returning null or undefined respectively.
  • Circular References: Objects referencing themselves directly or indirectly.
  • Arrays of Objects: Arrays containing objects that themselves have circular references.
  • Objects with Functions: Objects containing functions as properties (these should be omitted).
  • Date Objects: Date objects should be cloned correctly, preserving their values.
  • Regular Expressions: Regular expressions should be cloned correctly.

Examples

Example 1:

Input: { a: 1, b: { c: 2, d: 3 } }
Output: { a: 1, b: { c: 2, d: 3 } }
Explanation: A simple object with nested objects. The cloned object should have the same structure and values.

Example 2:

Input: { a: 1, b: [1, 2, { c: 3 }] }
Output: { a: 1, b: [1, 2, { c: 3 }] }
Explanation: An object with an array containing nested objects.

Example 3:

Input: { a: 1 }
Output: { a: 1 }
Explanation: A simple object.

Example 4: (Circular Reference)

Input: let obj = { a: 1 }; obj.b = obj; deepClone(obj)
Output: let clonedObj = deepClone(obj); clonedObj should be an object with a: 1 and b pointing to clonedObj itself.
Explanation: Demonstrates handling of a circular reference. The cloned object must maintain the circular reference.

Example 5: (Object with a function)

Input: { a: 1, b: function() { console.log("hello"); } }
Output: { a: 1 }
Explanation: The function 'b' should not be included in the cloned object.

Constraints

  • The input obj can be any valid JavaScript object, array, or primitive value.
  • The function must not modify the original object.
  • The function must handle circular references without causing a stack overflow or infinite loop.
  • The function should be reasonably performant for objects of moderate size (up to a few hundred nested levels). While absolute performance isn't the primary focus, avoid excessively inefficient algorithms.
  • The cloned object should not contain functions.

Notes

  • Consider using a WeakMap to keep track of objects that have already been cloned to handle circular references efficiently.
  • Recursion is a natural approach for deep cloning, but be mindful of potential stack overflow issues with deeply nested objects.
  • Think about how to handle different data types (primitive, array, object, function, Date, RegExp) appropriately.
  • The order of properties in the cloned object does not need to be identical to the original object.
  • Focus on correctness and handling circular references. Performance is secondary.
Loading editor...
javascript