
Object.freeze(), Object.seal(), and preventExtensions()

JavaScript objects are very flexible by default. You can add properties, remove properties, and change property values freely. That is convenient most of the time, but there are situations where you want tighter control.
That is where Object.freeze(), Object.seal(), and Object.preventExtensions() come in.
The trouble is that they sound similar enough to blur together when you first meet them. All three restrict an object in some way, but they do not impose the same rules.
Once you understand the differences, they become much less mysterious.
preventExtensions() is the lightest restriction
Object.preventExtensions() stops new properties being added to an object.
const user = { name: 'Sophie',};Object.preventExtensions(user);After that, you cannot add a new property such as role.
user.role = 'Editor';That will fail or be ignored depending on mode and context.
But existing properties can still be changed, and in many cases removable properties can still be deleted.
So preventExtensions() is really saying:
"this object should not grow any further".
seal() goes a step further
Object.seal() also prevents new properties being added, but it goes further by stopping existing properties from being deleted.
const user = { name: 'Sophie',};Object.seal(user);Now:
- you cannot add new properties
- you cannot remove existing properties
But you can still change the value of an existing writable property.
user.name = 'Ellie';That still works.
So seal() means the shape of the object is locked, but its existing values may still change.
freeze() is the strongest of the three
Object.freeze() applies the strongest top‑level restriction.
const user = { name: 'Sophie',};Object.freeze(user);Now:
- you cannot add properties
- you cannot delete properties
- you cannot change existing property values
At least, not at the top level.
That is why freeze() is usually the one people mean when they talk about making an object immutable. But there is an important caveat to that word.
All Three are Shallow
This is the part beginners most often miss.
These methods only lock the object itself at the level they are applied. They do not automatically freeze or seal nested objects.
For example:
const settings = { theme: 'dark', preferences: { compact: false, },};Object.freeze(settings);You cannot replace settings.theme, but the nested object can still be mutated:
settings.preferences.compact = true;That still works because preferences points to another object that has not itself been frozen.
This is why "frozen" does not necessarily mean "deeply immutable". It usually means "top‑level properties on this object are locked".
Why This Matters in Practice
These methods are useful when you want to:
- protect configuration objects from accidental shape changes
- prevent helper objects from gaining ad hoc properties
- communicate that a value should be treated as fixed
- make accidental mutation more obvious during development
They are less useful if you imagine they will automatically solve all state mutation problems in a larger nested object graph. They will not.
That means the real value is clarity and guard rails, not some magical universal immutability switch.
A Side‑By‑Side Comparison
Imagine this object:
const product = { id: 42, title: 'Notebook',};With preventExtensions():
product.price = 10is blockedproduct.title = 'Large Notebook'still works- deleting an existing property may still be allowed
With seal():
- adding new properties is blocked
- deleting existing properties is blocked
- changing existing property values still works
With freeze():
- adding new properties is blocked
- deleting existing properties is blocked
- changing existing property values is blocked
That progression is the easiest way to remember them: prevent growth, then lock shape, then lock top‑level values.
They Do Not Replace Disciplined Object Design
It is tempting to think these methods are the answer to all mutation risks. They are not.
If a codebase is hard to reason about because state is shared unpredictably, freezing a few objects does not automatically fix the deeper design issue. It may help catch some mistakes, but you still need good update patterns and clear ownership of data.
That is why these methods are best thought of as constraints, not architecture.
When freeze() is especially nice
freeze() is often helpful for objects that represent fixed definitions or options rather than living state.
For example:
const ROUTES = Object.freeze({ home: '/', articles: '/articles', contact: '/contact',});That communicates intent quite nicely. This object is meant to be read, not re‑authored halfway through the application.
That kind of usage is often clearer than applying freeze() reactively to ordinary changing state and expecting it to clean up the surrounding logic.
Checking the State of an Object
JavaScript also gives you helpers to inspect whether an object has been frozen, sealed, or made non‑extensible:
Object.isFrozen()Object.isSealed()Object.isExtensible()
Those are useful mostly for debugging, testing, or confirming assumptions in utilities.
Wrapping up
Object.preventExtensions(), Object.seal(), and Object.freeze() all restrict objects, but they do so at different levels. preventExtensions() stops growth, seal() locks the object's shape, and freeze() also blocks top‑level value changes. The most important practical caveat is that all three are shallow, so nested objects can still be mutable unless you handle them separately.
Key Takeaways
preventExtensions()blocks new properties from being added.seal()blocks additions and deletions but still allows changes to existing writable properties.freeze()blocks additions, deletions, and top‑level value changes.- All three are shallow rather than deep.
- These methods are useful guard rails, but they do not replace sensible state design.
Once you understand the progression from "cannot grow" to "cannot change", these APIs are much easier to choose between deliberately.
Related Articles

CSS visibility: Hiding Elements Without Affecting Layout. CSS

Why HTML Form Values are Always Strings in JavaScript. Why HTML Form Values are Always Strings in JavaScript

Building Custom Hooks in React. Building Custom Hooks in React

Enhancing Web Typography with text‑wrap: balance. Enhancing Web Typography with
text‑wrap: balance
Margin Collapse in CSS. Margin Collapse in CSS

Check If Your Site is Running on localhost. Check If Your Site is Running on
localhost
CSS Focus Styles for Keyboard Users Only. CSS Focus Styles for Keyboard Users Only

What Does a Software Engineer Do? What Does a Software Engineer Do?

JSON.parse() and JSON.stringify() Explained for Beginners. JSON.parse()andJSON.stringify()Explained for Beginners
Prefix Sums and Hash Maps: Solving 'Subarray Sum Equals K'. Prefix Sums and Hash Maps: Solving 'Subarray Sum Equals K'

Image Optimisation with next/image. Image Optimisation with
next/image
Null and undefined in JavaScript. nullandundefinedin JavaScript