You will be able to protect JavaScript objects from accidental changes by using Object.freeze and Object.seal, and explain the difference between shallow protection and deep immutability.
What it is
Protecting objects means limiting what can be changed after an object is created. JavaScript objects are mutable by default, so any property can be added, removed, or updated unless you restrict it. Object.freeze makes an object non-extensible and makes existing properties non-writable and non-configurable. Object.seal makes an object non-extensible and makes existing properties non-configurable, but their values can still change. Related terms include shallow protection, deep immutability, strict mode, and property descriptors.
Why it matters
- Prevents accidental mutation of configuration defaults.
- Makes shared objects safer when passed into functions.
- Helps debugging by failing loudly in strict mode.
- Supports API contracts where returned data should not be modified.
- Reduces bugs caused by hidden state changes in caches or constants.
Syntax or steps
The smallest useful pattern is:
- Create the object.
- Call
Object.freezeorObject.sealon it. - Use strict mode so invalid changes throw instead of failing silently.
"use strict";
const config = Object.freeze({
apiVersion: "v1",
timeout: 5000
});
config.timeout = 1000; // TypeError
Sealing is similar, but it allows existing property values to change:
"use strict";
const record = Object.seal({ id: 1, name: "Ada" });
record.name = "Grace"; // allowed
record.email = "grace@example.com"; // TypeError
Example
"use strict";
const defaults = Object.freeze({
theme: "dark",
features: {
beta: false
}
});
function applySettings(settings) {
settings.features.beta = true; // allowed
settings.theme = "light"; // TypeError
}
try {
applySettings(defaults);
} catch (error) {
console.log(error.message);
}
console.log(defaults.theme); // "dark"
console.log(defaults.features.beta); // true
This example shows that Object.freeze is shallow. The top-level property theme cannot be changed, so assigning "light" throws a TypeError. However, features is a nested object that was not frozen, so features.beta can still be changed.
Common mistakes
- Assuming freeze is deep. Fix: recursively freeze nested objects and arrays, or use a deep-freeze helper.
- Forgetting strict mode. Fix: use
"use strict"or ES modules; otherwise invalid changes may fail silently. - Confusing seal and freeze. Fix: remember that
Object.sealallows value changes, whileObject.freezedoes not. - Freezing objects that need updates. Fix: freeze constants and config defaults, not stateful application data.
When to use it
Choose the protection level based on how much mutability you want to allow.
| Method | Add new properties | Change existing values | Delete properties | Best for |
|---|---|---|---|---|
Object.preventExtensions |
No | Yes | Yes | Stopping shape growth |
Object.seal |
No | Yes | No | Fixed schema with mutable values |
Object.freeze |
No | No | No | Constants and config defaults |
Use Object.freeze for immutable constants. Use Object.seal when the object shape must stay fixed but values may update. Use Object.preventExtensions when only adding new properties should be blocked.
Practice
Guided exercise: create a frozen user object with a name property and a roles array. Try to change name and push a new role into roles.
"use strict";
const user = Object.freeze({
name: "Sam",
roles: ["viewer"]
});
try {
user.name = "Jordan";
} catch (error) {
console.log("name blocked");
}
user.roles.push("admin");
console.log(user.roles); // ["viewer", "admin"]
Expected result: changing name throws a TypeError, but pushing to roles succeeds because the array is not frozen.
Challenge: write a small recursive function that freezes nested plain objects and arrays. Hint: check typeof value === "object" and value !== null, then freeze each property before freezing the object itself.
Quick check
Question: If an object is frozen, can you change a property of a nested object? Answer: Yes, unless the nested object is also frozen; Object.freeze is shallow.
Summary
Object.freeze and Object.seal protect JavaScript objects by controlling extensibility and property descriptors. Freeze is best for shallow immutable constants, while seal is best for fixed-shape mutable records. Remember that protection is shallow unless you recursively freeze nested values.