Object.freeze()
freeze(obj) The Object.freeze() method makes an object completely immutable—you can’t add, remove, or modify any properties. It returns the same object that was passed in (not a copy).
Syntax
Object.freeze(obj)
Object.freeze() operates on the object in place — it does not create a copy. After freezing, every property descriptor on the object is marked non-writable and non-configurable, and the object itself becomes non-extensible. The return value is the same reference you passed in, which lets you freeze and assign in a single expression.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
obj | object | — | The object to freeze. |
Return Value
Returns the object that was passed in (not a copy).
Examples
Basic freeze usage
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
Object.freeze(config);
// These operations all fail silently in non-strict mode
config.apiUrl = 'https://other.com'; // Ignored
config.newProp = 'value'; // Ignored
delete config.timeout; // Ignored
// In strict mode, each would throw a TypeError
console.log(config.apiUrl); // 'https://api.example.com'
// Verify frozen state
Object.isFrozen(config); // true
The frozen object retains its original values. Any attempt to modify it fails without warning in sloppy mode, but throws in strict mode. This is why you should always verify the frozen state with Object.isFrozen() when the immutability guarantee matters — silent failures in non-strict code are easy to miss during development.
Freeze vs seal comparison
Object.seal() and Object.freeze() are easy to confuse because both prevent structural changes, but they differ in one critical way. Sealing allows you to change existing property values; freezing does not. The example below shows a frozen object rejecting value assignments that a sealed object would accept.
const frozen = Object.freeze({ value: 1 });
// Frozen: can't change values
frozen.value = 2;
console.log(frozen.value); // 1
// Can't add new properties
frozen.newProp = 'x';
console.log(frozen.newProp); // undefined
Common patterns
Freezing is most useful for values that should never change after initialization — configuration objects, shared constants, and lookup tables. The patterns below show how to apply Object.freeze() to real-world data structures in production code and library code.
Configuration constants:
const APP_CONFIG = Object.freeze({
VERSION: '1.0.0',
MAX_RETRIES: 3,
API_BASE: 'https://api.app.com'
});
// Attempting to modify throws in strict mode
// Great for values that should never change
Configuration constants like APP_CONFIG benefit from freezing because the values represent facts about the application that should not be reassigned. If any code path accidentally mutates a frozen constant, it either throws immediately (strict mode) or fails silently — both outcomes are safer than letting the mutation propagate.
Deep freezing for nested objects:
function deepFreeze(obj) {
Object.freeze(obj);
for (const key of Object.keys(obj)) {
if (obj[key] && typeof obj[key] === 'object') {
deepFreeze(obj[key]);
}
}
return obj;
}
const data = {
user: { name: 'Alice' },
settings: { theme: 'dark' }
};
deepFreeze(data);
data.user.name = 'Bob'; // Fails
data.settings.theme = 'light'; // Fails
Caveats
Object.freeze() has two behaviors that catch developers off guard: it only freezes the top level of an object, and it works on arrays too.
Shallow freeze only:
A single call to Object.freeze() does not descend into nested objects. The outer reference becomes immutable, but the inner objects remain fully mutable. This is the most common gotcha — you freeze a configuration object but later code modifies a nested property without error because the inner object was never frozen.
const obj = {
nested: { value: 1 }
};
Object.freeze(obj);
obj.nested.value = 2; // This works! Nested objects aren't frozen
console.log(obj.nested.value); // 2
A simple Object.freeze() doesn’t affect nested objects. You need deep freezing for that — either the recursive approach shown above or a library like Immutable.js if you need guaranteed deep immutability at scale.
Arrays are also affected:
Object.freeze() works on arrays because arrays are objects in JavaScript. Once frozen, you cannot push, pop, shift, unshift, splice, or reassign elements. This is useful for constant lookup tables and enum-like lists, but it means you lose all mutating array methods.
const arr = [1, 2, 3];
Object.freeze(arr);
arr.push(4); // TypeError in strict mode
arr[0] = 99; // Fails
arr.length = 10; // Fails
Freezing arrays prevents any modification to their contents or length.
What freeze does internally
Object.freeze() makes the object non-extensible (no new properties), marks every own data property as non-writable (values cannot change), and marks every own property as non-configurable (properties cannot be deleted or have their descriptors changed). Accessor properties (getters/setters) are left as-is since they are already described by functions rather than a writable value. The object itself is returned unchanged; freeze() does not create a copy.
Object.isFrozen()
Object.isFrozen(obj) returns true if the object is frozen. An object is frozen when it is non-extensible and every own property is non-configurable and non-writable. Empty objects sealed with Object.seal() are also considered frozen (there are no writable properties to protect).
const obj = { x: 1 };
console.log(Object.isFrozen(obj)); // false
Object.freeze(obj);
console.log(Object.isFrozen(obj)); // true
Freeze is irreversible
Once frozen, an object cannot be unfrozen. There is no Object.unfreeze(). This is by design — freeze is intended as a permanent guarantee, not a temporary lock. If you need to update the object later, create a new object from its properties using the spread operator or Object.assign(). The old frozen object remains unchanged, and the new one carries the updated values.
const frozen = Object.freeze({ a: 1, b: 2 });
const updated = { ...frozen, b: 99 }; // new object, not mutating frozen
Performance note
Frozen objects can allow JavaScript engines to apply more aggressive optimizations on property lookups, since the engine can assume the object’s shape never changes. For configuration objects or constants that are used frequently, freezing them can provide a small performance benefit.
When freeze() is the better choice
Object.freeze() is the right choice when an object should be treated as a true constant. That means no new properties, no removed properties, and no value changes on the existing own properties. It is a strong fit for configuration objects, shared defaults, and data that should not be edited after construction.
It is stricter than Object.seal(), so use it when the object’s contents should be stable rather than just its shape. If you still need to update fields later, freeze is the wrong tool; a sealed or ordinary object will fit better.
See Also
Object.seal()— Allow value changes but prevent structural changesObject.keys()— Get an object’s own property keysObject.isFrozen()— Check if an object is frozen