Deep vs. Shallow Cloning in JavaScript: Implementation Patterns and Pitfalls

Primitive vs. Reference Types

Category Examples Storage Copy Semantics
Primitive string, number, boolean, undefined, null, symbol, bigint Call-stack Value
Reference Object, Array, Date, RegExp, Function Heap Referenec (pointer)

Primitives are copied by value; references are copied by address. Two variables that share the same address point to the same heap object.

let x = 42;
let y = x;        // value copy
y = 99;
console.log(x);   // 42

let a = { k: 1 };
let b = a;        // address copy
b.k = 2;
console.log(a.k); // 2

Shallow Clone

A shallow clone duplicates only the first layer. Nested objects remain shared.

1. Manual iteration

function shallow(obj) {
  return Array.isArray(obj) ? [...obj] : Object.assign({}, obj);
}

const src = { x: 1, y: { z: 2 } };
const dst = shallow(src);
dst.x = 9;
dst.y.z = 9;
console.log(src.x); // 1
console.log(src.y.z); // 9  (shared)

2. Built-ins

const dst1 = Object.assign({}, src);
const dst2 = { ...src };

Deep Clone

1. Naïve recursion

function deepClone(o, seen = new WeakMap()) {
  if (o === null || typeof o !== 'object') return o;
  if (o instanceof Date) return new Date(o);
  if (o instanceof RegExp) return new RegExp(o);
  if (seen.has(o)) return seen.get(o);   // handle cycles

  const result = Array.isArray(o) ? [] : {};
  seen.set(o, result);
  for (const k of Reflect.ownKeys(o)) {
    result[k] = deepClone(o[k], seen);
  }
  return result;
}

const src = { a: 1, b: { c: 2 } };
src.self = src;                       // cycle
const dst = deepClone(src);
dst.b.c = 99;
console.log(src.b.c); // 2

2. Structured clone (modern)

const dst = structuredClone(src);

3. JSON round-trip

const dst = JSON.parse(JSON.stringify(src));

Limitations:

  • Drops functions, undefined, symbol, BigInt
  • Loses prototype chain (dst.constructor === Object)
  • Cannot handle circular references

4. Library helpers

// Lodash
import cloneDeep from 'lodash/cloneDeep';
const dst = cloneDeep(src);

// jQuery
const dst = $.extend(true, {}, src);

Commmon Misconceptions

Array.prototype.slice and concat create shallow copies:

const arr1 = [1, [2, 3]];
const arr2 = arr1.slice();
arr2[1][0] = 99;
console.log(arr1[1][0]); // 99

Quick Reference

Method Depth Handles cycles Copies functions Prototype
{...obj} 1
Object.assign 1
JSON
structuredClone
Custom recursion

Tags: javascript clone deep-clone shallow-clone memory-management

Posted on Tue, 30 Jun 2026 17:24:10 +0000 by kliopa10