Immutability
Immutability
Section titled “Immutability”Introduction
Section titled “Introduction”Immutability means data never changes after creation. Instead of modifying existing data, you create new copies with the desired changes.
Why Immutability Matters
Section titled “Why Immutability Matters”Mutable state is a common source of bugs:
// Mutable approach — dangerous!const user = { name: 'Alice', age: 25 };function celebrateBirthday(u) { u.age += 1; // mutates the original!}celebrateBirthday(user);console.log(user.age); // 26 — side effect!
// Immutable approach — safefunction celebrateBirthday(u) { return { ...u, age: u.age + 1 };}const updated = celebrateBirthday(user);console.log(user.age); // 25 — unchangedconsole.log(updated.age); // 26Spread Operator for Objects
Section titled “Spread Operator for Objects”const base = { name: 'Alice', role: 'admin' };
// Add a propertyconst withEmail = { ...base, email: 'alice@example.com' };
// Update a propertyconst promoted = { ...base, role: 'super-admin' };
// Nested update (spread each level)const state = { user: { name: 'Bob', settings: { theme: 'dark' } }};
const newState = { ...state, user: { ...state.user, settings: { ...state.user.settings, theme: 'light' } }};Spread Operator for Arrays
Section titled “Spread Operator for Arrays”const numbers = [1, 2, 3];
// Add to endconst added = [...numbers, 4]; // [1, 2, 3, 4]
// Add to beginningconst prepended = [0, ...numbers]; // [0, 1, 2, 3]
// Remove an itemconst removed = numbers.filter(n => n !== 2); // [1, 3]
// Update an itemconst updated = numbers.map(n => n === 2 ? 20 : n); // [1, 20, 3]
// Sort without mutationconst sorted = [...numbers].sort((a, b) => b - a);Array Methods That Return New Arrays
Section titled “Array Methods That Return New Arrays”| Method | Returns new? | Mutates original? |
|---|---|---|
.map() | ✅ | ❌ |
.filter() | ✅ | ❌ |
.concat() | ✅ | ❌ |
.slice() | ✅ | ❌ |
.sort() | ❌ (returns sorted array but mutates!) | ✅ |
.reverse() | ❌ | ✅ |
.push() | ❌ | ✅ |
.splice() | ❌ | ✅ |
Object.freeze
Section titled “Object.freeze”Prevents mutation at runtime:
const config = Object.freeze({ apiUrl: 'https://api.example.com', timeout: 5000,});
config.timeout = 3000; // Fails silently in strict mode / throws in strict modeconsole.log(config.timeout); // 5000
// Note: Object.freeze is shallow — nested objects can still be mutatedconst state = Object.freeze({ user: { name: 'Alice' }});state.user.name = 'Bob'; // This works! (shallow freeze)console.log(state.user.name); // 'Bob'Immutable Libraries
Section titled “Immutable Libraries”For deeply nested data, consider libraries:
- Immer: Uses a proxy-based
produceAPI - Immutable.js: Provides persistent data structures
// Immer exampleimport { produce } from 'immer';
const state = { user: { name: 'Alice', settings: { theme: 'dark' } } };
const nextState = produce(state, draft => { draft.user.settings.theme = 'light';});// Deeply nested immutable update without spreads!Summary
Section titled “Summary”- Never mutate data — create new copies
- Use spread (
...) for shallow updates - Use
.map(),.filter(),.concat()for arrays - Watch out:
.sort()and.reverse()mutate! Object.freeze()for shallow protection- Immer for complex nested updates