Object.freeze()
Genuine immutability, with two large caveats: it reaches only one level down, and outside strict mode a blocked write fails without saying anything.
Demo
The output is a pair: whether the object itself is frozen, and whether its n property is. The first case is the whole lesson — the outer object reports true while the nested object reports FALSE. Freezing protects only the top level, so config.n.x = 99 still succeeds on a frozen config. The second and third cases return true for n because primitives are immutable by definition and count as frozen; that makes the predicate look reassuring on flat objects and useless on nested ones. The array case behaves like the object case: arrays are objects, and they are left mutable.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| object | any | yes | The object to freeze. Primitives are returned unchanged and count as already frozen, so freezing one is a silent no-op rather than an error. |
Return value
object — The SAME object, now frozen — not a frozen copy. Properties cannot be added, removed, reordered or changed.
Common patterns
export const COLORS = Object.freeze({red: "#f00"});
const deepFreeze = o => { Object.values(o).forEach(v => { if (v && typeof v === "object") deepFreeze(v); }); return Object.freeze(o); };
const next = {...state, count: state.count + 1};
Examples
Pitfalls
const c = Object.freeze({db: {host: "x"}}); c.db.host = "y"; c.db.host
deepFreeze(c);
const o = Object.freeze({a: 1}); o.a = 2; o.a
"use strict"; const o = Object.freeze({a: 1}); o.a = 2
const o = {a: 1}; o.a = 2; o.a
const o = Object.freeze({a: 1});
Object.isFrozen(Object.freeze(42))
typeof v === "object" && v !== null
When to use
- Exported constants and lookup tables
- Catching accidental mutation during development
- Enforcing that a shared object is not modified by consumers
- Nested data → freeze recursively, or do not rely on it
- You want immutable UPDATES → spread into a new object
- Hot paths — some engines deoptimise frozen objects in polymorphic code
- You only want to block adding properties → seal or preventExtensions
Notes
FAQ
Recurse over the values yourself — there is no built-in. Watch for cycles, which will otherwise recurse forever, and remember that freezing is irreversible.
const deepFreeze = o => { Object.values(o).forEach(v => { if (v && typeof v === "object" && !Object.isFrozen(v)) deepFreeze(v); }); return Object.freeze(o); };