Object.defineProperty()
Defines or modifies one property on an object with exact control over its attributes.
Object.defineProperty(obj, prop, descriptor)- Added in JavaScript ES5
Parameters
| Parameter | Type | Description |
|---|---|---|
$obj | Object | The object on which to define the property. |
$prop | string | symbol | The key of the property to be defined or modified. |
$descriptor | Object | The descriptor for the property. A data descriptor may carry value and writable; an accessor descriptor may carry get and set. Either kind may carry enumerable and configurable. |
Return value
ObjectThe object that was passed in, with the property added or modified.
Errors
Throws a TypeError if the descriptor mixes data attributes (value, writable) with accessor attributes (get, set), or if it attempts to redefine a non-configurable property.
Changelog
- PHP ES5Introduced. Every attribute omitted from the descriptor defaults to false (or undefined for value/get/set), not to the values a plain assignment would produce.
Object.defineProperty() is the low-level form of property creation. A plain assignment like obj.x = 1 always produces a property that is writable, enumerable and configurable. defineProperty() lets you say otherwise, and this is where nearly every surprise comes from: anything you leave out of the descriptor defaults to false. Define a property with only { value: 42 } and you have created a frozen, invisible, permanent property.
When to reach for it
- Attaching internal bookkeeping (an id, a cached result) that must not appear in
Object.keys(),for...in, spread orJSON.stringify(). - Adding a getter or setter to an object that already exists, which object literal syntax cannot do.
- Locking a single field without freezing the whole object.
What trips people up
Writing to a non-writable property fails silently in sloppy mode and throws a TypeError in strict mode — and module code and class bodies are always strict, so the same line behaves differently depending on where you paste it.
Once a property is configurable: false you cannot redefine it, cannot delete it, and cannot make it enumerable again. There is no undo. Set configurable: true unless permanence is the actual goal.
Example
When to use it
- Attach a non-enumerable id to a record so it stays out of JSON.stringify output
- Add a computed getter to an object returned by a library you do not control
- Make one configuration field read-only without freezing the entire object
Frequently asked questions
Why is my property missing from Object.keys() after defineProperty?
Because enumerable defaults to false when you omit it. Pass enumerable: true explicitly if the property should appear in Object.keys(), for...in, spread and JSON.stringify().
Why does assigning to a defineProperty property do nothing?
writable defaults to false, so the assignment is ignored in sloppy mode and throws a TypeError in strict mode. Add writable: true to the descriptor.
What is the difference between defineProperty and a normal assignment?
A normal assignment creates a writable, enumerable, configurable property and triggers any inherited setter; defineProperty defaults every attribute to false and defines the property directly, ignoring setters on the prototype chain.
More examples
Defining a getter
An accessor descriptor uses get/set instead of value/writable. The getter re-runs on every read, so changing first is reflected immediately. Marking it enumerable makes it show up in Object.keys() alongside the data properties.
const user = { first: "Ada", last: "Lovelace" };
Object.defineProperty(user, "fullName", {
get() {
return `${this.first} ${this.last}`;
},
enumerable: true,
configurable: true,
});
console.log(user.fullName); // "Ada Lovelace"
user.first = "Augusta";
console.log(user.fullName); // "Augusta Lovelace"
console.log(Object.keys(user)); // [ 'first', 'last', 'fullName' ]Non-writable in strict mode
Under strict mode the assignment throws rather than failing quietly, and delete returns false because the property is not configurable. Without the "use strict" directive the assignment would be a silent no-op, which is why these bugs are so hard to spot.
"use strict";
const locked = {};
Object.defineProperty(locked, "version", { value: 1, enumerable: true });
try {
locked.version = 2;
} catch (err) {
console.log(err.constructor.name); // "TypeError"
}
console.log(locked.version); // 1
console.log(delete locked.version); // false — not configurable
console.log(locked.version); // 1
Discussion