Integrity Levels (preventExtensions, seal, freeze)
Three levels of object mutability restriction, each blocking a specific, increasing set of operations — freeze is shallow.
Object.preventExtensions(obj) / Object.seal(obj) / Object.freeze(obj)
Purpose
JavaScript provides three increasingly strict integrity levels, each locking down a specific, growing set of operations on an object.
The three levels
const obj = { count: 1 };
Object.preventExtensions(obj); // blocks adding new properties only
Object.seal(obj); // + blocks deleting/reconfiguring existing properties
Object.freeze(obj); // + blocks reassigning existing writable properties too
| Operation | preventExtensions | seal | freeze |
|---|---|---|---|
| Add a new property | Blocked | Blocked | Blocked |
| Delete an existing property | Allowed | Blocked | Blocked |
| Reconfigure a descriptor | Allowed | Blocked | Blocked |
| Reassign an existing writable value | Allowed | Allowed | Blocked |
Behavior notes
- All three are shallow — locking an object’s own properties has no effect on a nested object referenced by one of them, which remains fully mutable unless separately locked.
Object.isExtensible/isSealed/isFrozencheck the corresponding state.- In strict mode, a blocked operation throws a
TypeError; outside strict mode, it’s silently ignored — a common source of a “why didn’t my mutation stick” bug in non-strict code. Object.freezeon a primitive value (a number, a string) is a no-op that returns the value unchanged — primitives are already immutable.
Common mistakes
- Assuming
Object.freezedeep-freezes nested structures — a manual recursive freeze is needed for that. - Freezing an object non-strict and being confused when a mutation attempt neither throws nor visibly fails — it silently does nothing.
Runtime notes
Core ECMAScript, identical behavior across every current browser and Node version.