Object.freeze() in JavaScript
Object.freeze() is a built-in JavaScript method that "freezes" an object and makes it fully immutable: properties cannot be changed, deleted or added. The method returns the same object rather than a copy, and it only applies to the top level of the structure.
Theory
TL;DR
Object.freeze(obj)makes an object immutable and returns the same object.- You cannot overwrite, add or delete properties.
- You cannot change the
configurableandwritabledescriptors. - To check the state:
Object.isFrozen(obj). - The freeze is shallow: nested objects stay mutable.
Object.seal()forbids adding and deleting but allows changing values;Object.preventExtensions()only forbids adding new properties.
Quick example
const user = {
name: 'Alice',
age: 25
};
Object.freeze(user);
user.name = 'Oleh'; // will not change
user.city = 'Kyiv'; // cannot add a new one
delete user.age; // cannot delete
console.log(user); // { name: 'Alice', age: 25 }Syntax and checking the state
Object.freeze(obj)| Argument | Description |
|---|---|
obj | The object to "freeze" |
The method returns the same object, now with frozen properties, so const frozen = Object.freeze(user) gives you not a copy but a second name for user.
After Object.freeze() the object becomes immutable:
- properties cannot be overwritten;
- they cannot be deleted, and new ones cannot be added;
configurableandwritablecannot be changed.
You can check the state with a separate method:
console.log(Object.isFrozen(user)); // trueThe execution mode matters: in ordinary (sloppy) code a write attempt is silently ignored, while under 'use strict' or inside an ES module it throws a TypeError. That is exactly why the bug is easy to miss in an old script and immediately visible in a modern module.
The freeze is shallow
If there are nested objects inside, they stay mutable:
const user = {
name: 'Alice',
address: {
city: 'Kyiv'
}
};
Object.freeze(user);
user.address.city = 'Lviv'; // this works
console.log(user.address.city); // "Lviv"So Object.freeze() protects only the top level. What is frozen is the reference to address: you cannot replace it with another object, but you are free to change something inside that same object. For a "deep" freeze you have to freeze recursively.
Deep freeze
function deepFreeze(obj) {
Object.freeze(obj);
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key] !== null && !Object.isFrozen(obj[key])) {
deepFreeze(obj[key]);
}
}
return obj;
}
const user = {
name: 'Alice',
address: { city: 'Kyiv' }
};
deepFreeze(user);
user.address.city = 'Lviv'; // will not change
console.log(user.address.city); // "Kyiv"Now the object and all of its contents are completely "frozen". The !Object.isFrozen(obj[key]) check is not only about speed: without it a circular reference (a.self = a) would cause infinite recursion.
freeze, seal and preventExtensions
| Method | What it does | Can you change values | Can you add or delete properties |
|---|---|---|---|
Object.freeze() | Freezes completely | No | No |
Object.seal() | Forbids adding and deleting but allows changing values | Yes | No |
Object.preventExtensions() | Forbids adding new properties | Yes | Adding no, deleting existing ones yes |
An example of freeze versus seal:
const user = { name: 'Alice' };
Object.seal(user);
user.name = 'Oleh'; // allowed
delete user.name; // not allowed
Object.freeze(user);
user.name = 'Max'; // not allowedSummary table:
| Property | Behaviour |
|---|---|
Object.freeze() | makes the object completely immutable |
| Forbidden | adding, deleting or changing properties |
| Works shallowly | nested objects stay mutable |
| Check | Object.isFrozen(obj) |
Common mistakes
- Assuming the whole object graph is frozen.
Object.freeze()protects only the top level; an array inside a frozen object happily acceptspush(). - Counting on an error when writing. In sloppy mode an assignment to a frozen property silently does nothing, so the bug survives unnoticed until you move to modules or
'use strict'. - Confusing
freezewithseal.Object.seal()only fixes the set of keys, the values of existing properties can still be changed. - Thinking the method returns a copy. It returns the same object, so every old reference becomes frozen too.
- Freezing an object you will need to update. For application state it is better to build a new object (
{ ...state, city: 'Lviv' }) than to try to mutate a frozen one. - Forgetting about circular references in
deepFreeze(). Without theObject.isFrozen()check the recursion loops forever and blows the stack.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.