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:
value: The current value assigned to the property. Defaults toundefined.writable: A boolean indicating whether the property’s value can be changed using an assignment operator (truemeans modifiable).enumerable: A boolean indicating whether the property will appear in enumeration operations, such asfor...inloops orObject.keys().configurable: A boolean indicating whether the property can be deleted from the object and whether its descriptor attributes (such as changingenumerableor switching to an accessor property) can be modified.
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:
get: The function that serves as a getter for the property, orundefinedif no getter is specified.set: The function that serves as a setter for the property, orundefinedif no setter is specified.enumerable: A boolean indicating if the property appears during iteration.configurable: A boolean indicating if the property can be deleted or converted into a data descriptor.
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 }