Immutability
Immutability means not changing existing values directly.
Instead of modifying data in place, you create updated copies.
This is especially important when working with objects, arrays, and application state.
Mutable Update
const user = {
name: "Alice",
points: 10,
};
user.points = 20;
console.log(user); // { name: "Alice", points: 20 }This changes the original object.
That is mutation.
Immutable Update
const user = {
name: "Alice",
points: 10,
};
const updatedUser = {
...user,
points: 20,
};
console.log(user); // { name: "Alice", points: 10 }
console.log(updatedUser); // { name: "Alice", points: 20 }The original object stays unchanged.
Why Immutability Helps
Immutable updates make code easier to reason about.
They help you avoid bugs caused by unexpected shared references.
They are useful in:
- React state updates
- reducers
- undo/redo systems
- caching
- tests
- data transformation pipelines
Arrays and Immutability
Some array methods mutate the original array.
const numbers = [3, 1, 2];
numbers.sort();
console.log(numbers); // [1, 2, 3]sort() mutates.
Create a copy first:
const numbers = [3, 1, 2];
const sorted = [...numbers].sort();
console.log(numbers); // [3, 1, 2]
console.log(sorted); // [1, 2, 3]Non-Mutating Array Methods
These methods return new arrays:
| Method | Purpose |
|---|---|
map() |
transform each item |
filter() |
keep some items |
slice() |
copy part of an array |
concat() |
combine arrays |
toSorted() |
sort without mutating |
toReversed() |
reverse without mutating |
toSpliced() |
splice without mutating |
Examples:
const numbers = [1, 2, 3];
const doubled = numbers.map((number) => number * 2);
const even = numbers.filter((number) => number % 2 === 0);
console.log(numbers); // [1, 2, 3]
console.log(doubled); // [2, 4, 6]
console.log(even); // [2]Mutating vs Non-Mutating Methods
| Mutates original | Returns new value |
|---|---|
push() |
concat() |
pop() |
slice(0, -1) |
splice() |
toSpliced() |
sort() |
toSorted() |
reverse() |
toReversed() |
If your environment does not support newer methods like toSorted(), use copies with spread.
const sorted = [...numbers].sort();Immutable Object Updates
Add or replace a property:
const user = {
id: 1,
name: "Alice",
};
const updatedUser = {
...user,
role: "admin",
};Remove a property with destructuring:
const user = {
id: 1,
name: "Alice",
password: "secret",
};
const { password, ...safeUser } = user;
console.log(safeUser); // { id: 1, name: "Alice" }Updating Nested Data
Spread only copies one level deep.
const state = {
user: {
name: "Alice",
address: {
city: "Delhi",
},
},
};To update nested data immutably, copy each level you change.
const updatedState = {
...state,
user: {
...state.user,
address: {
...state.user.address,
city: "Mumbai",
},
},
};This is verbose, but predictable.
Immutability Is About Behavior
const does not make objects immutable.
const user = {
name: "Alice",
};
user.name = "Bob"; // allowedconst prevents reassignment of the variable.
It does not freeze the object.
Object.freeze()
Object.freeze() prevents direct changes to an object.
const settings = Object.freeze({
theme: "dark",
});
settings.theme = "light"; // ignored or error in strict modeBut it is shallow.
const state = Object.freeze({
user: {
name: "Alice",
},
});
state.user.name = "Bob"; // nested object can still changeBest Practices
Prefer returning new arrays and objects.
Avoid mutating function arguments.
Use map, filter, and reduce for transformations.
Copy each nested level that changes.
Remember that spread creates shallow copies.
Use mutation intentionally when it is local and safe.
Common Mistakes
Mistake 1: Mutating State With push
items.push(newItem);Immutable version:
const updatedItems = [...items, newItem];Mistake 2: Thinking Spread Deep Copies Everything
const copy = { ...state };
copy.user.name = "Bob";If user is nested, both objects still share the same nested object.
Mistake 3: Thinking const Means Immutable
const protects the variable binding, not the object contents.
Summary
Immutability means creating updated copies instead of changing existing data.
- Mutation changes existing arrays or objects.
- Immutable updates return new arrays or objects.
- Spread syntax is useful but shallow.
- Copy every nested level you update.
constdoes not make objects immutable.- Immutability helps keep state predictable.