← Back to Blog
Code Snippets July 13, 2026

Deep Merge & Deep Clone in JS (Circular Ref Detection)

💻 CODE SNIPPET — July 13, 2026

Deep Merge & Deep Clone in JS (Circular Ref Detection)

When Object.assign(), spread, and structuredClone() aren't enough.

deepClone(obj) — Full deep copy via WeakMap. Preserves Dates, Regexps, Maps, Sets, Errors, TypedArrays, Buffers, custom classes. Detects circular refs.

deepMerge(a, b, options) — Recursive merge with array strategies: 'replace' (default), 'concat' (dedup), or custom fn(left, right, key).

function deepClone(obj, seen = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (seen.has(obj)) return seen.get(obj);
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  if (obj instanceof Map) { const m = new Map(); obj.forEach((v,k) => m.set(deepClone(k,seen), deepClone(v,seen))); return m; }
  if (obj instanceof Set) { const s = new Set(); obj.forEach(v => s.add(deepClone(v,seen))); return s; }
  if (ArrayBuffer.isView(obj)) return obj.slice(0);
  const clone = Object.create(Object.getPrototypeOf(obj));
  seen.set(obj, clone);
  for (const [k, v] of Object.entries(obj)) clone[k] = deepClone(v, seen);
  return clone;
}

function deepMerge(a, b, { arrays = 'replace' } = {}) {
  const r = { ...a };
  for (const [k, v] of Object.entries(b)) {
    if (v && typeof v === 'object' && !Array.isArray(v) && a[k] && typeof a[k] === 'object')
      r[k] = deepMerge(a[k], v, { arrays });
    else if (arrays === 'concat' && Array.isArray(a[k]) && Array.isArray(v))
      r[k] = [...new Set([...a[k], ...v])];
    else r[k] = v;
  }
  return r;
}


Zero deps — browsers, Node, Deno. Bookmark for when structuredClone() isn't available.

\#javascript \#deepmerge \#deepclone