JavaScript Object.getOwnPropertyDescriptor Guide

JavaScript provides powerful reflection capabilities that allow developers to look beyond surface-level key-value pairs and inspect the internal configuration of an object’s properties. This article explains how the built-in Object.getOwnPropertyDescriptor() method works, the specific attributes contained within the property descriptor objects it returns, and how to use this tool to determine whether properties are writable, enumerable, configurable, or backed by getter and setter functions.

Understanding Object.getOwnPropertyDescriptor

The Object.getOwnPropertyDescriptor() static method accepts two arguments: the target object and the property name (a string or symbol) to inspect. It returns a property descriptor object describing the configuration of a specific property directly present on the target object. If the property does not exist directly on the object (even if it exists on its prototype chain), the method returns undefined.

const descriptor = Object.getOwnPropertyDescriptor(targetObject, propertyKey);

Property Descriptor Attributes

In JavaScript, properties are categorized as either data properties or accessor properties. The descriptor returned by Object.getOwnPropertyDescriptor() reflects these differences.

1. Data Descriptors

A data descriptor describes a property that holds a concrete value. It contains the following four attributes:

Example: Inspecting a Standard Data Property

const user = {
  name: "Alice"
};

const nameDescriptor = Object.getOwnPropertyDescriptor(user, "name");
console.log(nameDescriptor);
// Output:
// {
//   value: 'Alice',
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

By default, properties created via standard object literal syntax or direct assignment have writable, enumerable, and configurable set to true.

2. Accessor Descriptors

An accessor descriptor describes a property defined by a getter-setter pair rather than a static value. It contains the following attributes:

Example: Inspecting an Accessor Property

const account = {
  balance: 100,
  get formattedBalance() {
    return `$${this.balance}`;
  }
};

const balanceDescriptor = Object.getOwnPropertyDescriptor(account, "formattedBalance");
console.log(balanceDescriptor);
// Output:
// {
//   get: [Function: get formattedBalance],
//   set: undefined,
//   enumerable: true,
//   configurable: true
// }

Inspecting Non-Enumerable and Built-in Properties

Object.getOwnPropertyDescriptor() is particularly useful for inspecting hidden or non-enumerable properties, such as the length property on arrays or properties defined using Object.defineProperty().

const arr = [1, 2, 3];
const lengthDescriptor = Object.getOwnPropertyDescriptor(arr, "length");

console.log(lengthDescriptor);
// Output:
// {
//   value: 3,
//   writable: true,
//   enumerable: false,
//   configurable: false
// }

Here, enumerable: false explains why length does not appear when looping over array keys with Object.keys(arr).

Inspecting Inherited Properties

Because Object.getOwnPropertyDescriptor() only inspects properties owned directly by the specified object, checking an inherited property returns undefined. To inspect inherited attributes, you must retrieve the descriptor directly from the prototype where it is defined:

const child = Object.create({ inheritedProp: "Hello" });

console.log(Object.getOwnPropertyDescriptor(child, "inheritedProp")); 
// Output: undefined

const proto = Object.getPrototypeOf(child);
console.log(Object.getOwnPropertyDescriptor(proto, "inheritedProp")); 
// Output: { value: 'Hello', writable: true, enumerable: true, configurable: true }