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 | ∞ | ✅ | ✅ | ✅ |