Skip to main content

Read-only properties

In JavaScript a property can be made read-only, meaning it cannot be changed, deleted or redefined. There are several ways to do it, from configuring a single property to freezing the entire object.

Theory

TL;DR

  • Object.defineProperty(obj, key, { writable: false }) makes one specific property read-only.
  • Adding configurable: false also forbids deleting it or redefining its descriptor.
  • Object.freeze(obj) freezes the whole object: no changes, no additions, no deletions.
  • Object.seal(obj) is weaker: values can change, the object's shape cannot.
  • A getter without a setter gives you a computed read-only property.
  • In sloppy mode a failed write is ignored silently, in strict mode it throws TypeError.

Quick example

javascript
const user = {}; Object.defineProperty(user, 'name', { value: 'Alice', writable: false, // forbids changing the value enumerable: true, // the property shows up when iterating configurable: false // cannot be deleted or redefined }); console.log(user.name); // "Alice" user.name = 'Bob'; // does not change the value console.log(user.name); // "Alice" delete user.name; // does not delete the property console.log(user.name); // "Alice"

Object.defineProperty

This is the main way to define a property with exactly the attributes you want. writable: false makes it read-only, and configurable: false additionally prevents deletion and any further redefinition through defineProperty.

You can inspect the attributes with the descriptor:

javascript
console.log(Object.getOwnPropertyDescriptor(user, 'name'));

It prints:

javascript
{ value: 'Alice', writable: false, enumerable: true, configurable: false }

Note that when you create a property with Object.defineProperty() and omit the attributes, they all default to false. So the property ends up read-only whether you meant it or not.

Object.freeze

When every property has to become immutable at once, freeze the object:

javascript
const user = { name: 'Alice', age: 25 }; Object.freeze(user); user.name = 'Bob'; // does not change the value user.city = 'Kyiv'; // cannot add a new property delete user.age; // cannot delete an existing one console.log(user); // { name: 'Alice', age: 25 }

Object.freeze() does two things:

  • every existing property gets writable: false and configurable: false;
  • the object becomes non-extensible, so new properties cannot be added.

Freezing is shallow: nested objects stay mutable, and user.profile.city = 'Lviv' still works. A deep freeze has to walk the object graph recursively.

A getter without a setter

For a computed value you do not need a descriptor at all: a property with get but no set is read-only by definition.

javascript
const user = { firstName: 'Alice', lastName: 'Smith', get fullName() { return `${this.firstName} ${this.lastName}`; } }; console.log(user.fullName); // "Alice Smith" user.fullName = 'Bob Jones'; // has no effect

Object.seal

When a full freeze is too much and you only want to lock the shape of the object:

javascript
const user = { name: 'Alice' }; Object.seal(user); user.name = 'Bob'; // allowed delete user.name; // not allowed user.age = 30; // not allowed

Unlike freeze(), seal() lets you change the values of existing properties but forbids changing the set of keys.

Comparison

ApproachWhat it doesChange a valueAdd or delete
Object.defineProperty(obj, key, { writable: false })Makes one property read-onlyNoYes
Object.freeze(obj)Freezes the whole objectNoNo
Object.seal(obj)Forbids adding and deleting propertiesYesNo
Getter without a setterComputed read-only propertyNoYes

In one line: to make a property read-only use Object.defineProperty(obj, 'key', { value: val, writable: false }), or freeze the whole object with Object.freeze(obj).

Common mistakes

  • Expecting an exception in sloppy mode. Outside strict mode, assigning to a read-only property simply does nothing and raises no error, so the bug is easy to miss. In modules and classes, where strict mode is always on, the same code throws TypeError.
  • Confusing freeze with seal. seal() does not make properties read-only, it only locks the set of keys.
  • Assuming Object.freeze() is deep. Nested objects and arrays remain fully mutable after the parent is frozen.
  • Forgetting configurable: false. With writable: false but configurable: true the property can be redefined by another Object.defineProperty() call, which defeats the protection.
  • Leaving attributes implicit. In Object.defineProperty() an omitted writable, enumerable or configurable defaults to false, so the property may unexpectedly disappear from Object.keys() and from loops.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.