Skip to content
  • Immutability & Copying · JavaScript

Immutability & Copying

LearnViaNotes @learnvianotes· 2 min read

Assignment copies references. To copy data you choose between a shallow copy (top level only) and a deep copy (everything) — and immutable updates build new objects instead of mutating old ones.

Shallow copies share nested objects

JavaScript
const original = { name: "Asha", skills: ["js"], meta: { level: 1 } };
const shallow = { ...original };
shallow.name = "Ravi";          // top level: independent
shallow.skills.push("css");     // nested: SHARED
shallow.meta.level = 99;
console.log(original);
console.log(shallow.skills === original.skills);
Output
{ name: 'Asha', skills: [ 'js', 'css' ], meta: { level: 99 } }
true
Shallow copy toolsArraysObjects
Spread[...arr]{...obj}
Built-insarr.slice(), Array.from(arr), arr.concat()Object.assign({}, obj)

Deep copy with structuredClone

JavaScript
const original = {
  when: new Date(0),
  tags: new Set(["a"]),
  lookup: new Map([["k", { deep: true }]]),
  nested: { list: [1, [2, 3]] },
};
original.self = original;              // cycles are fine
const clone = structuredClone(original);
clone.nested.list[1].push(4);
console.log(original.nested.list, clone.nested.list);
console.log(clone.when instanceof Date, clone.tags, clone.self === clone);
Output
[ 1, [ 2, 3 ] ] [ 1, [ 2, 3, 4 ] ]
true Set(1) { 'a' } true
JavaScript
class Point { constructor(x) { this.x = x; } norm() { return Math.abs(this.x); } }
const copy = structuredClone(new Point(-3));
console.log(copy, copy instanceof Point);
try {
  structuredClone({ fn() {} });
} catch (e) {
  console.log(e.name);
}
Output
{ x: -3 } false
DataCloneError
MethodDates / Map / SetCyclesFunctionsClass instances
Spread / assignshared refs (shallow)sharedcopied (ref)prototype lost for spread
JSON.parse(JSON.stringify())Date → string, Map/Set → {}throwsdroppedplain object
structuredClone✔ preserved✔throws DataCloneErrorplain object (prototype lost)
Custom recursive / lodash cloneDeep✔✔ (with a map)kept by referenceconfigurable

Immutable update patterns

JavaScript
const state = { user: { name: "Asha", prefs: { theme: "light" } }, todos: [{ id: 1, done: false }] };

const next = {
  ...state,
  user: { ...state.user, prefs: { ...state.user.prefs, theme: "dark" } },
  todos: state.todos.map((t) => (t.id === 1 ? { ...t, done: true } : t)),
};
console.log(state.user.prefs.theme, next.user.prefs.theme);
console.log(state.todos[0].done, next.todos[0].done);
console.log(state.user === next.user, state === next);
Output
light dark
false true
false false
Instead of (mutating)Write (immutable)
arr.push(x)[...arr, x]
arr.splice(i, 1)arr.filter((_, j) => j !== i) or arr.toSpliced(i, 1)
arr[i] = varr.with(i, v) or arr.map(...)
arr.sort()arr.toSorted()
obj.k = v{ ...obj, k: v }
delete obj.kconst { k, ...rest } = obj

Shallow copy vs deep copy?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report