W3docs

JavaScript Property Descriptors

Learn how JavaScript property flags (writable, enumerable, configurable) and descriptors work, including data vs accessor descriptors and defineProperty.

JavaScript's property flags and descriptors provide precise control over object properties, enabling robust and secure application development. This article explores these features in detail, providing practical insights and code examples to help you effectively manage property behavior.

Understanding JavaScript Property Attributes

JavaScript objects are collections of properties, and each property has associated attributes that define its behavior. These attributes, often referred to as property flags, include:

  • Writable: Determines if the property's value can be changed.
  • Enumerable: Controls if the property is visible during enumeration, such as in a for...in loop.
  • Configurable: Specifies whether the property can be deleted or modified.

These flags are crucial for controlling access to object properties, ensuring data integrity, and implementing encapsulation in JavaScript applications.

Diving Into Property Descriptors

Property descriptors provide detailed information about an object's property, encapsulating its value and flags. They are retrieved using Object.getOwnPropertyDescriptor(obj, propName) and set using Object.defineProperty(obj, propName, descriptor). A property descriptor object may contain:

  • value: The value associated with the property.
  • writable: Indicates if the property value can be changed.
  • enumerable: Denotes whether the property is enumerable.
  • configurable: Determines if the property descriptor can be changed and if the property can be deleted from the object.

Note: When you create a property the normal way (user.name = "John"), all three flags are set to true. But when defining a new property via Object.defineProperty, any unspecified flag defaults to false.


javascript— editable

Object.getOwnPropertyDescriptor only looks at the object's own properties. If you ask for a property the object inherits from its prototype (or a property that doesn't exist at all), it returns undefined.


javascript— editable

To learn more about how objects inherit properties, see Prototypal Inheritance.

Data Descriptors vs. Accessor Descriptors

So far we've described data descriptors, which store a value together with the writable flag. JavaScript also supports accessor descriptors, which replace value/writable with getter and setter functions:

  • get: a function called when the property is read (takes no arguments).
  • set: a function called when the property is assigned to (receives the new value).

A descriptor is either a data descriptor or an accessor descriptor — never both. Combining value/writable with get/set throws an error. Both kinds still share the enumerable and configurable flags.

Accessor properties are how you compute a value on read or validate one on write. Here we expose a fullName accessor backed by two data properties:


javascript— editable

For a fuller treatment of get/set syntax (including the shorthand inside object literals), see Property Getters and Setters. Because getters and setters run with this bound to the object, it also helps to understand Object Methods and "this".

Defining and Reading Many Properties at Once

For working with several properties in one step, JavaScript provides the plural counterparts of the methods above:

  • Object.defineProperties(obj, descriptors) defines multiple properties from a map of descriptors.
  • Object.getOwnPropertyDescriptors(obj) returns descriptors for all own properties (including non-enumerable and symbol keys) as a single object.

Object.getOwnPropertyDescriptors is especially useful for cloning an object with its flags — a plain spread or Object.assign copies values but resets every flag to true and skips accessors.


javascript— editable

Manipulating Property Flags

Understanding and manipulating property flags are crucial for effective JavaScript development. Let’s explore how to control these flags to fine-tune property behavior.

Making a Property Non-writable

Preventing modifications to a property ensures data consistency. This can be achieved by setting the writable flag to false.


javascript— editable

How the failed assignment behaves depends on the mode the code runs in. In non-strict mode, writing to a non-writable property fails silently: the assignment is simply ignored, no error is thrown, and execution continues — which can hide bugs. In strict mode ("use strict", and the default inside ES modules and class bodies), the same assignment throws a TypeError. The rule applies to any operation that violates a flag: deleting a non-configurable property or adding a property to a non-extensible object also fails silently in non-strict mode and throws in strict mode.


javascript— editable

Hiding a Property from Enumeration

Sometimes, it's necessary to hide properties from enumeration processes, such as for...in loops. This can be done by setting the enumerable flag to false.


javascript— editable

Preventing Property Deletion and Modification

To ensure a property remains a constant part of an object, set the configurable flag to false.


javascript— editable

Marking a property non-configurable is a one-way operation — there is no flag to make it configurable again, and you can no longer toggle enumerable or switch the property between a data and an accessor descriptor.

There are, however, two important exceptions while a property is non-configurable:

  • You may change writable from true to false (but not back from false to true).
  • If the property is still writable: true, you may change its value — either by direct assignment or via Object.defineProperty.

In other words, configurable: false locks the shape of the property, not necessarily its value. To truly freeze a property's value, set both configurable: false and writable: false.


javascript— editable

Higher-Level APIs Built on These Flags

You rarely need to set flags one property at a time. JavaScript ships three built-in methods that flip these flags across a whole object:

  • Object.preventExtensions(obj) — stops new properties from being added. Existing properties can still be changed or deleted.
  • Object.seal(obj) — prevents adding and deleting properties by marking every existing property configurable: false. Values can still change.
  • Object.freeze(obj) — the strictest: seals the object and makes every property writable: false, so nothing can be added, removed, or changed.

Each method has a matching check: Object.isExtensible, Object.isSealed, and Object.isFrozen. Note that these operate one level deep — Object.freeze does not freeze nested objects (it is a "shallow" freeze).


javascript— editable

Conclusion

Property flags and descriptors give you precise control over how object properties behave:

  • A data descriptor pairs a value with writable; an accessor descriptor uses get/set functions instead. Both share enumerable and configurable.
  • Read flags with Object.getOwnPropertyDescriptor (one property) or Object.getOwnPropertyDescriptors (all own properties); write them with Object.defineProperty or Object.defineProperties. Inherited and missing properties return undefined.
  • configurable: false is irreversible and locks the property's shape, though a still-writable property can have its value changed and its writable flag turned off.
  • Violating a flag fails silently in non-strict mode but throws a TypeError in strict mode.
  • Reach for Object.freeze, Object.seal, and Object.preventExtensions when you want to lock an entire object instead of individual flags.

Next steps: dive into Property Getters and Setters for the accessor syntax, Object Methods and "this" for how this behaves inside them, and Prototypal Inheritance to see how property lookup walks the prototype chain.

Practice

Practice
In JavaScript, what are the characteristics that can be defined by property descriptors?
In JavaScript, what are the characteristics that can be defined by property descriptors?
Was this page helpful?