All Snippets Snippet JavaScript

Deep Clone Object

ORIGINAL { id: 101, meta: {...} } CLONE (NEW REF) { id: 101, meta: {...} } Independent Memory Allocation

Create a fully independent copy of nested objects where modifying the clone never affects the original.

D
Kunwar "AKA" AJ Sharing what I have learned
Feb 21, 2026 2 min JavaScript

The Pattern

deepClone.js
function deepClone(obj) {
    // Handle primitives and null
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    // Handle Date
    if (obj instanceof Date) {
        return new Date(obj.getTime());
    }

    // Handle Array
    if (Array.isArray(obj)) {
        return obj.map(item => deepClone(item));
    }

    // Handle Object
    const cloned = {};
    for (const key of Object.keys(obj)) {
        cloned[key] = deepClone(obj[key]);
    }
    return cloned;
}

What Happens Under the Hood

Let us trace what happens when you clone a nested config object:

execution-flow.txt
Input: { db: { host: "localhost", ports: [5432, 5433] } }

Step 1: deepClone({ db: { host: "localhost", ports: [5432, 5433] } })
  → obj is Object → create new {}
  → key "db" → recurse

Step 2: deepClone({ host: "localhost", ports: [5432, 5433] })
  → obj is Object → create new {}
  → key "host" → recurse

Step 3: deepClone("localhost")
  → string is primitive → return "localhost" (new copy)

Step 4: deepClone([5432, 5433])
  → obj is Array → map each element
  → deepClone(5432) → primitive → return 5432
  → deepClone(5433) → primitive → return 5433
  → return NEW array [5432, 5433]

Result: completely independent copy — no shared references

The critical difference from Object.assign or the spread operator is that those only create shallow copies. If you modify a nested property on the copy, it modifies the original too because they share the same inner reference. Deep clone walks every level and creates new objects at each depth.

Why This Matters

shallow-vs-deep.txt
Shallow copy (spread operator):
  const copy = { ...original };
  copy.db.host = "remote";
  console.log(original.db.host);  // "remote" — MUTATED the original!

Deep clone:
  const copy = deepClone(original);
  copy.db.host = "remote";
  console.log(original.db.host);  // "localhost" — original untouched

Usage Example

usage.js
// Safe state snapshot before mutation
const previousState = deepClone(appState);
applyUserChanges(appState);

// Undo? Restore from snapshot
if (userClicksUndo) {
    appState = previousState;
}

// Safe config per environment
const baseConfig = { db: { host: "localhost", pool: 5 } };
const prodConfig = deepClone(baseConfig);
prodConfig.db.host = "prod-db.internal";
prodConfig.db.pool = 20;
// baseConfig.db.host is still "localhost"

Use this for state snapshots, config duplication, and any case where you need to modify a copy without affecting the original. For simple flat objects, the spread operator is fine. For anything nested, you need a deep clone. Note: structuredClone() is now available natively in modern browsers and Node 17+, but this manual version works everywhere and handles the cases you control.