Skip to main content

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 configurable and writable descriptors.
  • 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

javascript
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

javascript
Object.freeze(obj)
ArgumentDescription
objThe 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;
  • configurable and writable cannot be changed.

You can check the state with a separate method:

javascript
console.log(Object.isFrozen(user)); // true

The 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:

javascript
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

javascript
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

MethodWhat it doesCan you change valuesCan you add or delete properties
Object.freeze()Freezes completelyNoNo
Object.seal()Forbids adding and deleting but allows changing valuesYesNo
Object.preventExtensions()Forbids adding new propertiesYesAdding no, deleting existing ones yes

An example of freeze versus seal:

javascript
const user = { name: 'Alice' }; Object.seal(user); user.name = 'Oleh'; // allowed delete user.name; // not allowed Object.freeze(user); user.name = 'Max'; // not allowed

Summary table:

PropertyBehaviour
Object.freeze()makes the object completely immutable
Forbiddenadding, deleting or changing properties
Works shallowlynested objects stay mutable
CheckObject.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 accepts push().
  • 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 freeze with seal. 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 the Object.isFrozen() check the recursion loops forever and blows the stack.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.