How to Use Object.defineProperty in JavaScript

JavaScript’s Object.defineProperty() method provides granular control over how object properties behave by defining or modifying property descriptors. While standard assignment simply creates writable, enumerable, and configurable properties, Object.defineProperty() allows developers to explicitly set attributes such as writable, enumerable, configurable, value, get, and set. This guide explains how to use these descriptor attributes to customize object properties effectively.

Syntax and Basics

The method accepts three arguments: the target object, the property name (a string or Symbol), and the descriptor object.

Object.defineProperty(obj, prop, descriptor);

Property descriptors are divided into two main types: Data Descriptors and Accessor Descriptors. A descriptor must be one of these two types and cannot be both.


Data Descriptors

A data descriptor is a property that has a value, which may or may not be writable.

1. value

Defines the actual data stored in the property. It defaults to undefined.

2. writable

Controls whether the property value can be changed using an assignment operator (=). It defaults to false when defined via Object.defineProperty().

const user = {};

Object.defineProperty(user, 'id', {
  value: 101,
  writable: false // Prevents modification
});

user.id = 202; // In strict mode, this throws a TypeError; otherwise, it fails silently
console.log(user.id); // Output: 101

Shared Attributes

Both data and accessor descriptors share the following attributes:

1. enumerable

Determines whether the property shows up during enumeration, such as in for...in loops or Object.keys(). It defaults to false.

const car = {};

Object.defineProperty(car, 'engine', {
  value: 'V8',
  enumerable: false
});

console.log(Object.keys(car)); // Output: []
console.log(car.engine); // Output: 'V8'

2. configurable

Determines whether the property can be deleted from the object and whether its descriptor attributes can be modified later. It defaults to false.

const account = {};

Object.defineProperty(account, 'iban', {
  value: 'US123456789',
  configurable: false
});

delete account.iban; // Fails to delete
// Attempting to re-define the descriptor will throw a TypeError:
// Object.defineProperty(account, 'iban', { enumerable: true });

Accessor Descriptors

An accessor descriptor describes a property through a getter-setter pair instead of a fixed value.

1. get

A function that acts as a getter for the property. When the property is accessed, the function is called with no arguments and its return value becomes the property value. Defaults to undefined.

2. set

A function that acts as a setter for the property. When the property is assigned a value, the function is called with the assigned value as its argument. Defaults to undefined.

const person = {
  firstName: 'Jane',
  lastName: 'Doe'
};

Object.defineProperty(person, 'fullName', {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
  set(name) {
    const parts = name.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  },
  enumerable: true,
  configurable: true
});

console.log(person.fullName); // Output: Jane Doe
person.fullName = 'John Smith';
console.log(person.firstName); // Output: John

Summary of Default Values

When creating new properties using regular assignment (obj.prop = value), attributes default to true. When using Object.defineProperty(), omitted descriptor attributes default to false or undefined.

Attribute Default via Assignment Default via Object.defineProperty
value Specified value undefined
writable true false
enumerable true false
configurable true false
get undefined undefined
set undefined undefined