JS Object Protection
In this page:
Freezing an Object with Object.freeze()
Object.freeze(obj) locks an object completely -- no properties can be added, removed, or have their values changed afterward. It is commonly used for values meant to represent constants or fixed configuration that should never be accidentally modified.
Note: Freeze configuration objects, constants, or any data you specifically intend to be read-only after creation, to catch accidental mutation attempts.
Warning: In non-strict-mode code, attempting to mutate a frozen object fails silently with no error at all -- the assignment simply has no effect, which can hide a bug if you are not aware of this.
Example: Freezing an Object with Object.freeze()
const config = Object.freeze({ apiUrl: "/api" });
config.apiUrl = "/changed"; // silently fails
console.log(config.apiUrl);
Checking If an Object Is Frozen
Object.isFrozen(obj) returns true if the object has been frozen, letting code check this state before deciding whether to attempt a modification, or for debugging why an expected mutation did not take effect.
Note: Use Object.isFrozen() in debugging or defensive code when a mutation seems to be silently failing, to confirm whether the object is actually frozen.
Warning: A plain object literal is not frozen by default -- Object.isFrozen() returning true always means Object.freeze() (or an equivalent) was explicitly applied to it at some point.
Example: Checking If an Object Is Frozen
const obj = Object.freeze({ a: 1 });
console.log(Object.isFrozen(obj));
Sealing an Object with Object.seal()
Object.seal(obj) is a lighter restriction than freeze -- it prevents adding new properties or removing existing ones, but still allows changing the value of properties that already exist, useful when an object's shape should stay fixed while its data can still update.
Note: Use seal() over freeze() specifically when an object's set of properties should stay fixed, but the values themselves legitimately need to change over time.
Warning: Confusing seal() with freeze() is common -- remember seal() still allows value changes, only locking the object's shape (which properties exist), not its content.
Example: Sealing an Object with Object.seal()
const user = Object.seal({ name: "Sam" });
user.name = "Amit"; // allowed, value can change
// user.age = 30; // not allowed, cannot add new property
console.log(user);
The Shallow Nature of freeze() and seal()
Both Object.freeze() and Object.seal() only protect an object's own direct properties -- if one of those properties holds a nested object, that nested object remains completely unprotected and freely mutable, unless it is separately frozen or sealed too.
Note: To protect a nested structure completely, recursively freeze each nested object individually, or write (or use a small library providing) a deep-freeze helper function.
Warning: Assuming Object.freeze() protects an entire nested data structure is a very common misconception -- always verify whether the specific level you care about is actually frozen.
Example: The Shallow Nature of freeze() and seal()
const obj = Object.freeze({ nested: { value: 1 } });
obj.nested.value = 99; // still works! nested object is not frozen
console.log(obj.nested.value);
Choosing Between freeze(), seal(), and Neither
Use Object.freeze() for genuinely constant, fixed data (like an enum-style configuration); Object.seal() for data whose shape should stay fixed but whose values legitimately change; and leave an object entirely unrestricted when neither guarantee is actually needed.
Note: Reach for freeze/seal specifically to enforce an actual invariant you care about (like preventing accidental mutation of shared configuration), not as a default habit for every object.
Warning: Sealing or freezing an object that legitimately needs to be extended or restructured later can create confusing bugs where expected mutations silently fail.
Example: Choosing Between freeze(), seal(), and Neither
const MAX_USERS = Object.freeze({ limit: 100 }); // truly constant
const settings = Object.seal({ theme: "dark" }); // shape fixed, values can change
const data = { count: 0 }; // fully mutable
settings.theme = "light";
console.log(MAX_USERS, settings, data);
- Assuming Object.freeze() makes an object's nested objects immutable too -- like Object.assign() and spread, freeze() is shallow, only protecting the object's own direct properties.
- Expecting an attempted mutation of a frozen object to always throw an error -- outside strict mode, the mutation silently fails instead, which can hide a bug rather than surfacing it.
- Confusing Object.freeze() (locks everything: no add, remove, or change) with Object.seal() (allows changing existing values, but not adding or removing properties).
- Object.freeze(obj) prevents adding, removing, or changing any of an object's own properties.
- Object.seal(obj) prevents adding or removing properties, but still allows changing the values of existing ones.
- Both freeze() and seal() are shallow -- nested objects remain fully mutable unless frozen or sealed individually.
Object.freeze() and Object.seal() have been supported in every browser since ES5.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: