Sobes.tech
Middle+

What do you know about descriptors?

sobes.tech AI

Answer from AI

Descriptors in JavaScript are objects that describe various attributes of an object property, such as its value, writability, enumerability, and configurability. There are also descriptors with getters and setters.

There are two types of descriptors:

  • Data descriptors: Contain the value of the property.

    • value: The value of the property. Can be any valid data type.
    • writable: A boolean indicating if the property value can be changed (obj.prop = newValue). Defaults to false for accessor properties and true for data properties created by simple assignment.
    • enumerable: A boolean indicating if the property will be visible during property enumeration (e.g., in for...in loops or Object.keys()). Defaults to false for properties added with Object.defineProperty, and true for properties created by simple assignment.
    • configurable: A boolean indicating if the property attributes can be changed and if the property can be deleted. Defaults to false for properties added with Object.defineProperty (except value), and true for properties created by simple assignment. Once set as non-configurable, it cannot be changed back. You can decrease writability from true to false, but not vice versa on a non-configurable getter/setter.
  • Accessor descriptors: Define functions that are called when getting or setting the property value.

    • get: A function called when reading the property. Its result becomes the property value. It takes no arguments.
    • set: A function called when writing to the property. It takes one argument — the new value.
    • enumerable: A boolean (same as data descriptors).
    • configurable: A boolean (same as data descriptors).

Descriptors cannot be mixed: a property either has value and writable, or get and set.

Descriptors are used for lower-level property management than simple assignment, especially through methods like Object.defineProperty(), Object.defineProperties(), Object.getOwnPropertyDescriptor(), Object.getOwnPropertyDescriptors(), Object.create().

Example of creating a property with a data descriptor:

// Create an empty object
const obj = {};

// Define a 'name' property with a data descriptor
Object.defineProperty(obj, 'name', {
  value: 'Alice',
  writable: false, // Property cannot be changed
  enumerable: true, // Property will be enumerable
  configurable: false // Property attributes cannot be changed, property cannot be deleted
});

console.log(obj.name); // Outputs: Alice

// Attempt to change the property (ignored in strict mode or throws an error)
obj.name = 'Bob';
console.log(obj.name); // Outputs: Alice

// Attempt to delete the property (ignored or throws an error)
delete obj.name;
console.log(obj.name); // Outputs: Alice

Example of creating a property with an accessor descriptor:

// Create an object with private variables
const user = {
  _firstName: 'John', // Private variable by convention
  _lastName: 'Doe'
};

// Define a 'fullName' property with accessor descriptors
Object.defineProperty(user, 'fullName', {
  get() {
    // Getter: returns the full name
    return `${this._firstName} ${this._lastName}`;
  },
  set(value) {
    // Setter: splits the full name into parts and updates private variables
    const parts = value.split(' ');
    this._firstName = parts[0];
    this._lastName = parts[1];
  },
  enumerable: true, // Property will be enumerable
  configurable: true // Property attributes can be changed, property can be deleted
});

console.log(user.fullName); // Outputs: John Doe

user.fullName = 'Peter Pan';
console.log(user._firstName); // Outputs: Peter
console.log(user._lastName); // Outputs: Pan
console.log(user.fullName); // Outputs: Peter Pan

Using descriptors allows creating immutable properties, lazily computed properties, access-controlled properties (getters/setters), and more flexible object behavior.