Object.freeze() is how you enforce real immutability in JavaScript, going beyond what const alone provides. It prevents property addition, deletion, and reassignment — with an important caveat about nested objects.
1Object.freeze() | JavaScript Tutorial - In-Depth Guide Part 1
Object.freeze() prevents adding, removing, or reassigning any property on an object — attempts to do so are silently ignored (or throw in strict mode).
const config = Object.freeze({ env: 'prod' });
config.env = 'dev'; // ignored (or throws in strict mode)
config.env; // still 'prod'Locking an Object
2Object.freeze() | JavaScript Tutorial - In-Depth Guide Part 2
Object.freeze() is shallow — it only freezes the object's own top-level properties, not any nested objects they point to.
const state = Object.freeze({ user: { name: 'Al' } });
state.user.name = 'Bo'; // succeeds! nested object isn't frozenShallow Freeze
3Object.freeze() | JavaScript Tutorial - In-Depth Guide Part 3
A deep-freeze utility recursively freezes every nested object, guaranteeing true immutability throughout the structure.
function deepFreeze(obj) {
Object.values(obj).forEach((v) => {
if (v && typeof v === 'object') deepFreeze(v);
});
return Object.freeze(obj);
}Deep Freeze Utility
4Object.freeze() | JavaScript Tutorial - In-Depth Guide Part 4
In strict mode, attempting to mutate a frozen object throws a TypeError instead of silently failing — ES modules are strict mode by default.
'use strict';
const frozen = Object.freeze({ x: 1 });
frozen.x = 2; // TypeError: Cannot assign to read only propertyStrict Mode Throws
5Object.freeze() | JavaScript Tutorial - In-Depth Guide Part 5
Object.isFrozen() lets you check whether an object is frozen, useful for defensive assertions or tests.
Object.isFrozen(Object.freeze({})); // true
Object.isFrozen({}); // falseChecking Frozen State
6Step-by-Step Breakdown
Object.freeze() prevents adding, removing, or reassigning any property on an object — attempts to do so are silently ignored (or throw in strict mode).
Object.freeze() is shallow — it only freezes the object's own top-level properties, not any nested objects they point to.
Checkpoint: If you freeze an object with a nested object property, is that nested object also frozen?
- →Yes, freeze is deep by default
- →No, freeze only locks the top level
A deep-freeze utility recursively freezes every nested object, guaranteeing true immutability throughout the structure.
In strict mode, attempting to mutate a frozen object throws a TypeError instead of silently failing — ES modules are strict mode by default.
Checkpoint: In a strict-mode context (like an ES module), does mutating a frozen object throw an error?
- →Yes, it throws a TypeError
- →No, it always fails silently
Object.isFrozen() lets you check whether an object is frozen, useful for defensive assertions or tests.
Next, we'll explore 'Object.seal()'.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Freezing Static Accessibility Configuration Prevents Accidental Regression
Freezing a shared object of standard ARIA role/label constants used across a component library prevents one component's code from accidentally mutating values relied upon by every other component.
SEO Implications
- 1
No Direct SEO Effect
Object.freeze() is a data-integrity tool; its only SEO relevance is indirect, by preventing accidental mutation bugs in shared configuration that could otherwise corrupt rendered content.
Best Practices
Freeze Shared Constants and Configuration Objects
Any object exported as a shared constant across modules is a good candidate for Object.freeze(), preventing one module from accidentally mutating state relied upon elsewhere.
Use a Deep-Freeze Utility for Nested Constant Data
A single top-level Object.freeze() call gives a false sense of security for nested structures; use a recursive deep-freeze (or a library) when the entire structure must be immutable.
Frequent Bugs
Assuming Object.freeze() protects an entire nested configuration object, then discovering a nested object's properties were still silently mutated elsewhere in the codebase.
Use a deep-freeze utility for nested structures, or explicitly freeze each nested object individually.
Relying on a silent, non-throwing mutation attempt on a frozen object outside of strict mode, missing the bug entirely because no error surfaced.
Write code as ES modules (strict mode by default) or add "use strict" explicitly, so accidental mutation attempts throw and are caught during development or testing.
Real-World Examples
Freezing Application-Wide Constants
A codebase exported a shared object of HTTP status code constants that should never be mutated by any importing module.
export const HTTP_STATUS = Object.freeze({
OK: 200,
NOT_FOUND: 404,
SERVER_ERROR: 500,
});