DEV Community

How to create deep clone of an object using javascript?

Hi there ๐Ÿ™Œ

What knowledge can you learn through this article?

  1. Differentiate primitives and references
  2. how to use typeof or instanceof?
  3. how to accurly to get types?
  4. how to traversal map and set?
  5. how to use weakmap?
  6. understand Prototype Chain
  7. what is the different between Reflect.ownKeys() and hasOwnProperty?
  8. what is the different between Reflect.ownKeys() and Object.keys?
  9. what is the different between Reflect.ownKeys() and for...in...?

How the Custom Function Works

  1. primitive value: directly return string,number,boolean etc;
  2. Circular reference: Use weakMap to track cloned object, preventing infinite recursion when an object references itself;
  3. Special Types:
    • Date: clone using new Date(original);
    • RegExp: clone using new RegExp(original);
    • Map/Set: create an instances and recursively clone their value;
  4. Array/Object: create an instances and recursively clone nested properties
function deepClone(original, cloned = new WeakMap()) {
  // 1. Handle primitive values (direct copy)
  if (original === null || typeof original !== "object") {
    return original;
  }
  // or you can use instanof 
  // original instanceof Object


  // 2. Handle circular references (avoid infinite recursion)
  if (cloned.has(original)) {
    return cloned.get(original);
  }

  // 3. Handle special object types
  const type = Object.prototype.toString.call(original);

  // Clone Date
  if (type === "[object Date]") {
    const clone = new Date(original);
    cloned.set(original, clone);
    return clone;
  }

  // Clone RegExp
  if (type === "[object RegExp]") {
    const clone = new RegExp(original.source, original.flags);
    clone.lastIndex = original.lastIndex; // Preserve lastIndex
    cloned.set(original, clone);
    return clone;
  }

  // Clone Map
  if (type === "[object Map]") {
    const clone = new Map();
    cloned.set(original, clone); // Cache before recursion to handle circular refs
    original.forEach((value, key) => {
      clone.set(deepClone(key, cloned), deepClone(value, cloned));
    });
    return clone;
  }

  // Clone Set
  if (type === "[object Set]") {
    const clone = new Set();
    cloned.set(original, clone);
    original.forEach(value => {
      clone.add(deepClone(value, cloned));
    });
    return clone;
  }

  // 4. Clone arrays or plain objects
  // Create a new instance (array or object)
  const clone = Array.isArray(original) ? [] : {};
  cloned.set(original, clone); // Cache to handle circular references

  // Recursively clone nested properties
  Reflect.ownKeys(original).forEach(key => {
    clone[key] = deepClone(original[key], cloned);
  });

  return clone;
}

// Test Cases
const original = {
  name: "Bob",
  birth: new Date("1990-01-01"),
  regex: /hello/g,
  map: new Map([["a", 1]]),
  set: new Set([1, 2, 3]),
  nested: { x: 10, y: [20, 30] }
};

// Add a circular reference (original references itself)
original.self = original;

const cloned = deepClone(original);

// Verify independence
cloned.nested.x = 100;
cloned.map.set("b", 2);

console.log(original.nested.x); // 10 (original unchanged)
console.log(cloned.nested.x);  // 100 (clone modified)
console.log(cloned.self === cloned); // true (circular reference preserved)

Enter fullscreen mode Exit fullscreen mode

Top comments (0)