webroad.online
  1. 1Web
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Tooling
  8. 8React
  9. 9State management
  10. 10Next.js
  11. 11Forms
  12. 12Data and backend
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Animations
  16. 16Testing
  17. 17Architecture
JavaScript · Lesson 10 of 17

References and immutability

Value vs reference, immutable updates, shallow vs deep copy.

Updated

What value and reference mean

When you assign a value to a variable, JS does one of two things, depending on the type:

By value By reference
Types primitives: string, number, boolean, null, undefined objects, arrays, functions
What gets copied the value itself the address of the same value in memory
Effect two independent copies two names for the same object
let a = 1
let b = a
b = 2
a          // 1 — independent copies

const x = { count: 1 }
const y = x            // y points to the same object as x
y.count = 2
x.count    // 2 — it's the same object!

An analogy: primitives are like a photocopy of a page; objects are like a link to a Google Doc — you send the link, not the document.

Comparison follows the same rule

'a' === 'a'            // true — compares values
{ a: 1 } === { a: 1 }  // false — two different objects, different addresses
const o = {}; o === o  // true — the same address

What immutability is

Immutable = you don't change the existing object, you create a new one with the change. The opposite is mutation — changing it in place.

// mutation
user.age = 31

// immutable
const updated = { ...user, age: 31 }

Why it matters — especially in React

React decides whether to re-render by comparing the old state with the new one by reference (Object.is). If you change the object in place, the reference is the same → React thinks nothing changed → the UI doesn't update.

// ✗ mutation: the same reference, no re-render
todos.push(newTodo)
setTodos(todos)

// ✓ a new array
setTodos([...todos, newTodo])

Other benefits: you can quickly check "did it change?", you get history (undo), and there are no hidden effects between parts of the code that share the same object.

Cheat sheet: immutable updates

Operation Array Object
add [...arr, x] { ...obj, key: v }
remove arr.filter(x => x.id !== id) const { key, ...rest } = obj
change one arr.map(x => x.id === id ? { ...x, done: true } : x) { ...obj, key: v }
sort arr.toSorted(fn) —
at an index arr.with(i, v) —
nested — { ...obj, address: { ...obj.address, city } }

Shallow copy vs deep copy

Shallow Deep
How { ...obj }, [...arr], Object.assign structuredClone(obj)
What it copies only the first level; the objects inside stay shared everything, at any depth
Cost small bigger
When state updates (you copy only what you change) when you really need an independent copy
const copy = { ...user }
copy.address.city = 'Denver'   // ✗ also changes user.address — it's the same object

const deep = structuredClone(user)
deep.address.city = 'Denver'   // ✓ user stays untouched

JSON.parse(JSON.stringify(x)) — the old hack; it loses Date, Map, undefined, functions. Use structuredClone.

Object.freeze

Object.freeze(obj) blocks changes (first level only). Useful for configuration constants, to catch accidental mutations.

Summary

  • Primitives are copied by value, objects by reference.
  • Immutable = create a new object instead of changing it; required for state in React.
  • Spread copies only the first level; structuredClone for a full copy.

Official sources

Exercises

Was this page helpful?

One tap — no account needed.