Immutable and mutable data
Mutable data can be changed after it has been created, while immutable data cannot be changed: a new value is produced instead. In JavaScript primitives are immutable, while objects, arrays and functions are mutable.
Theory
TL;DR
- Mutable: the object can be edited after creation:
object,array,function,Map,Set. - Immutable: the value cannot be edited, only a new one can be created: every primitive.
constpins the reference, not the contents of the object, soconst user = {}still allowsuser.name = '...'.- "Changing" a string or a number always creates a new value, the old one stays untouched.
Object.freeze()makes an object immutable at the top level, nested ones need a deep freeze.
Quick example
// mutable
const user = { name: 'Tim' };
user.name = 'Alex';
console.log(user); // { name: 'Alex' }
// immutable
let a = 'Hello';
a[0] = 'J';
console.log(a); // "Hello", nothing changedDefinitions
| Term | Meaning | The essence |
|---|---|---|
| Mutable | changeable | an object that can be modified after it is created |
| Immutable | unchangeable | data that cannot be modified, you can only create new data |
Mutable data
Objects and arrays in JavaScript are mutable structures.
const user = { name: 'Tim' };
user.name = 'Alex'; // we modify the object
console.log(user); // { name: 'Alex' }Even though user is declared with const, its contents (the object) can be modified, because const forbids changing the reference itself, not the data it points at.
The same goes for an array:
const numbers = [1, 2, 3];
numbers.push(4);
console.log(numbers); // [1, 2, 3, 4]Arrays are mutable, since we can add, remove and change elements.
Immutable data
Primitives in JavaScript are immutable. They are: string, number, boolean, null, undefined, symbol, bigint.
let a = 'Hello';
a[0] = 'J'; // an attempt to modify the string
console.log(a); // "Hello", unchangedStrings are immutable: "changing" one creates a new string.
The same with numbers:
let x = 5;
let y = x;
y = y + 1;
console.log(x); // 5
console.log(y); // 6Numbers are immutable too, they cannot be "changed", only a new value can be created.
How that looks in memory:
| Data type | Mutability | Where it is stored | Behaviour |
|---|---|---|---|
Primitive (string, number, boolean and others) | Immutable | Stack | a new value is created on "change" |
| Object, array, function | Mutable | Heap | modified through the reference |
let name = 'Ann';
name = name + 'a';STACK:
+------------------+
| name = "Ann" | <- the old string
| name = "Anna" | <- the new string (created from scratch)
+------------------+The string "Ann" is not modified, a new string "Anna" is created.
Why it matters
First, the behaviour on copying:
const user1 = { name: 'Tim' };
const user2 = user1;
user2.name = 'Alex';
console.log(user1.name); // "Alex", both changed (mutable)Second, reactive frameworks (React, Redux, Vue and others). Immutability is a key principle there:
- it lets you track state changes by comparing references;
- it makes undo/redo straightforward;
- it makes the code more predictable.
An example of the React approach:
// bad (mutating the state directly)
state.user.age++;
// good (an immutable update)
setState({ ...state, user: { ...state.user, age: state.user.age + 1 } });How to make an object immutable by hand
const user = Object.freeze({
name: 'Tim',
age: 25
});
user.age = 30; // will not work
console.log(user.age); // 25Object.freeze() makes an object immutable at the top level. Nested properties have to be "deeply frozen":
function deepFreeze(obj) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key]);
}
});
return Object.freeze(obj);
}
const data = deepFreeze({
user: { name: 'Tim', skills: ['JS'] }
});
data.user.name = 'Alex'; // will not changeThe summary table:
| Category | Examples | Mutability | Behaviour |
|---|---|---|---|
| Immutable | string, number, boolean, null, undefined, symbol, bigint | cannot be changed | a new copy is created |
| Mutable | object, array, function, Map, Set | can be changed | modified "in place" |
In short:
- Immutable: cannot be changed, only a new value can be created.
- Mutable: can be changed "in place".
- Primitives are immutable, objects and arrays are mutable.
- Immutability makes code more reliable, especially in React and Redux.
Common mistakes
- Confusing
constwith immutability.constis about reassigning the variable, not about the contents of the object. - Assuming
Object.freeze()protects the whole structure. It is shallow, nested objects stay mutable. - Not noticing that in non-strict mode a write to a frozen object is silently ignored, while under
'use strict'it throws aTypeError. - Mutating state in React (
state.items.push(x)) and then wondering why the component did not re-render: a reference comparison sees no difference. - Believing array methods are immutable.
push,splice,sortandreversemutate the original; the immutable counterparts areconcat,slice,toSortedandtoReversed.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.