Object.defineProperty()
The low-level way to add a property, and the one place in the language where omitting an option means "off". A property defined this way is invisible to Object.keys unless you say otherwise.
Demo
The pair is the object own keys and the value read back. With enumerable false — the DEFAULT if you leave it out — the keys array is empty while o.a still returns the value. The property genuinely exists and is readable; it is simply invisible to Object.keys, Object.entries, JSON.stringify, the spread operator and for...in. That asymmetry is the single most surprising thing about this method, and it is why a property added here can appear to vanish from serialised output while working perfectly in code.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| object | object | yes | The object to modify. It is mutated and returned. |
| key | string | symbol | yes | The property key. |
| descriptor | object | yes | Either a DATA descriptor with value and writable, or an ACCESSOR descriptor with get and set. Never both. enumerable and configurable apply to either, and every omitted flag defaults to false. |
Return value
object — The same object, modified in place. The property is created or redefined according to the descriptor.
Common patterns
Object.defineProperty(o, "_cache", {value: new Map(), writable: true});
Object.defineProperty(o, "full", { get() { return `${this.first} ${this.last}`; }, enumerable: true, });
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
Examples
Pitfalls
const o = {}; Object.defineProperty(o, "a", {value: 1}); JSON.stringify(o)
Object.defineProperty(o, "a", {value: 1, enumerable: true, writable: true, configurable: true})
Object.defineProperty(o, "a", {value: 1}); Object.defineProperty(o, "a", {value: 2})
Object.defineProperty(o, "a", {value: 1, configurable: true})
Object.defineProperty({}, "a", {value: 1, get: () => 2})
Object.defineProperty({}, "a", {get: () => 2})
const p = {set a(v) { console.log("set"); }}; const o = Object.create(p); Object.defineProperty(o, "a", {value: 1})
o.a = 1
When to use
- Properties that should not appear in JSON or in Object.keys
- Read-only or non-deletable properties
- Getters and setters added after the object exists
- Copying properties with their descriptors intact
- An ordinary property → plain assignment or an object literal
- Several properties at once → Object.defineProperties
- You want everything locked → Object.freeze is simpler
- Genuinely private state → a # private field in a class
Notes
FAQ
Because enumerable defaults to false. JSON.stringify, Object.keys, Object.entries, spread and for...in all skip non-enumerable properties. Add enumerable: true if the property is part of the object visible data.
Object.defineProperty(o, "a", {value: 1, enumerable: true});