Property Descriptors
Introduction
Every JavaScript property has metadata, not just a value. A property descriptor tells the engine whether a property can be reassigned, enumerated, reconfigured, read through a getter, or written through a setter.
This topic turns object properties from a surface-level syntax feature into an internals model you can use to explain Object.freeze, Object.seal, getters, non-enumerable fields, and many interview traps.
Why This Matters
Descriptors are the hidden control panel behind object APIs. Interviewers use them to test whether you know why Object.keys skips some properties, why assignment to a read-only property may fail, and how frameworks expose computed values without storing them directly.
Theory
Data descriptors
A data property has a value and descriptor flags:
| Flag | Meaning | Default in defineProperty |
|---|---|---|
writable | Can assignment change the value? | false |
enumerable | Does it appear in Object.keys and for...in? | false |
configurable | Can the descriptor be changed or the property deleted? | false |
Object literal properties are usually writable, enumerable, and configurable. Object.defineProperty is intentionally stricter: omitted flags default to false.
Accessor descriptors
An accessor property has get and/or set functions instead of value and writable. Reading calls the getter. Assigning calls the setter. A descriptor cannot be both a data descriptor and an accessor descriptor.
Configurable is the strongest lock
If configurable: false, you generally cannot delete the property or change its descriptor shape. Some narrow changes are allowed, such as changing writable from true to false, but not back to true.
Strict mode vs sloppy mode
Writing to a non-writable property throws in strict mode and is silently ignored in sloppy mode. Interview answers should mention both behaviours when the snippet depends on assignment failure. Safer runtime APIs such as Reflect.defineProperty return false instead of relying on mode-dependent assignment behaviour.
Inspecting descriptors
Use Object.getOwnPropertyDescriptor(obj, key) for one property and Object.getOwnPropertyDescriptors(obj) for all own properties. This is essential when cloning objects without losing getters, setters, or enumerability flags.
Visual Diagrams
Property descriptor
|
+-- data: value + writable + enumerable + configurable
|
+-- accessor: get + set + enumerable + configurableA property is either data-backed or accessor-backed, never both at the same time.
Code Examples
Define a non-enumerable read-only property
Omitted descriptor flags default to false, which is different from an object literal.
Getter and setter descriptors
Accessor properties compute their value through functions. They are useful for validation, derived values, and lazy calculation.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1const obj = {};2 3Object.defineProperty(obj, 'x', {4 value: 15});6 7console.log(Object.keys(obj).length);8console.log(obj.x);9console.log(Object.getOwnPropertyDescriptor(obj, 'x').writable);Predict the output #2
1let writes = 0;2const obj = {};3 4Object.defineProperty(obj, 'value', {5 get: function () {6 return writes * 2;7 },8 set: function (next) {9 writes = next;10 },11 enumerable: true12});13 14obj.value = 5;15console.log(obj.value);16console.log(Object.keys(obj).join(','));Coding Exercises
Create a hidden immutable id
MediumImplement attachId(obj, id) so it adds an id property that can be read, does not appear in Object.keys, cannot be reassigned, and cannot be deleted or reconfigured.
Interview Questions
1What are `writable`, `enumerable`, and `configurable`?
writable controls whether assignment can change a data property's value. enumerable controls whether the property appears in APIs such as Object.keys and for...in. configurable controls whether the property can be deleted or have its descriptor changed. With Object.defineProperty, omitted flags default to false.
Follow-ups
- Can a non-configurable property ever be changed?
- How does strict mode affect writes to non-writable properties?
2How are getter/setter properties different from normal value properties?
Getter/setter properties are accessor descriptors. They do not store a value or have a writable flag. Reading the property calls get; assigning calls set. A descriptor cannot combine value with get or set.
Quiz
1. What is the default value of `enumerable` when omitted in `Object.defineProperty`?
2. Which descriptor shape is invalid?
Summary
- Property descriptors describe both the value mechanism and metadata flags of a property.
- `Object.defineProperty` defaults omitted flags to `false`.
- Data descriptors use `value` and `writable`; accessor descriptors use `get` and `set`.
- `configurable: false` prevents deletion and most descriptor changes.
Cheat Sheet
Inspect one: Object.getOwnPropertyDescriptor(obj, key)
Inspect all: Object.getOwnPropertyDescriptors(obj)
Data descriptor: value, writable, enumerable, configurable
Accessor descriptor: get, set, enumerable, configurable
defineProperty defaults: writable: false, enumerable: false, configurable: false.