Property Descriptors in JavaScript
Introduction
Every object property has hidden attributes: Is it writable? Enumerable in loops? Configurable for deletion? Object.defineProperty and Object.getOwnPropertyDescriptor expose these controls—used by frameworks and for defining getters without accidental overwrites.
Prerequisites
Default Descriptor
Data properties created in literals are usually writable, enumerable, and configurable:
// Normal assignment
const item = { price: 10 };
item.price = 12;
console.log(item.price);Read Descriptor
// Inspect attributes
const user = { name: "Ada" };
const desc = Object.getOwnPropertyDescriptor(user, "name");
console.log(desc);Typical data descriptor fields: value, writable, enumerable, configurable.
defineProperty — Control Behavior
// Non-writable property
const config = {};
Object.defineProperty(config, "apiUrl", {
value: "https://api.example.com",
writable: false,
enumerable: true,
configurable: false,
});
config.apiUrl = "https://other";
console.log(config.apiUrl);Silent failure in sloppy mode; TypeError in strict mode when assigning non-writable fields.
Accessor Descriptor (Getter / Setter)
// Computed property with getter
const account = {
_balance: 0,
};
Object.defineProperty(account, "balance", {
get() {
return this._balance;
},
set(value) {
if (value < 0) throw new Error("negative balance");
this._balance = value;
},
enumerable: true,
configurable: true,
});
account.balance = 100;
console.log(account.balance);Class getter/setter syntax is sugar over this model.
enumerable: false Hides from for...in
const obj = { visible: 1 };
Object.defineProperty(obj, "hidden", {
value: 2,
enumerable: false,
});
for (const key in obj) {
console.log(key);
}
console.log(Object.keys(obj));Object.assign and spread copy enumerable own properties only.
Object.defineProperties
// Batch define
const meta = {};
Object.defineProperties(meta, {
version: { value: "1.0.0", writable: false },
build: { value: 42, writable: false },
});
console.log(meta.version, meta.build);Mini Example: Read-Only ID Field
function createRecord(id, title) {
const record = { title };
Object.defineProperty(record, "id", {
value: id,
writable: false,
enumerable: true,
configurable: false,
});
return record;
}
const row = createRecord(101, "Widget");
console.log(row.id, row.title);FAQ
Descriptors vs private # fields?
Private fields are language-level privacy; descriptors are per-property flags on ordinary objects.
Can I change a non-configurable property?
Not delete or reconfigure; may still change value if writable: true.
Why learn this as a beginner?
Helps debug library behavior and read docs for Object.freeze and similar APIs.