Understanding Object.defineProperty() in JavaScript: Defaults, Behaviors, and Key Differences

JavaScript provides several ways to define object properties, but one of the most powerful and sometimes confusing methods is Object.defineProperty(). This method allows fine-grained control over property attributes like writability, enumerability, and configurability.

In this blog post, we'll explore Object.defineProperty(), understand its default behavior, and analyze why the same code can produce different outputs based on how properties are initially defined.

1. What is Object.defineProperty()?

Object.defineProperty() allows us to define or modify properties of an object with detailed control over how they behave.

Syntax:

Object.defineProperty(obj, propertyName, descriptor)
  • obj: The object on which to define the property.

  • propertyName: The name of the property to be defined or modified.

  • descriptor: An object defining property attributes.

Property Descriptor Attributes:

AttributeDefault ValueDescription
valueundefinedThe actual value of the property.
writablefalseIf true, the property can be changed using assignment (obj.prop = newValue).
enumerablefalseIf true, the property appears in loops like for...in and Object.keys().
configurablefalseIf true, the property can be deleted or redefined.

2. Understanding writable, enumerable, and configurable

1. writable: Determines whether a property’s value can be changed.

  • If writable: true:

    • The property value can be reassigned.
  • If writable: false:

    • The property value is read-only.
const obj = {};
Object.defineProperty(obj, 'name', {
    value: 'Hridoy',
    writable: false
});

console.log(obj.name); // Output: "Hridoy"

obj.name = 'Bob';
console.log(obj.name); // Output: "Hridoy" (cannot change because writable is false)

2. enumerable: Determines whether a property appears in loops (for...in, Object.keys(), Object.entries()).

  • If enumerable: true:

    • The property is visible during iteration.
  • If enumerable: false:

    • The property is hidden from enumeration methods but can still be accessed directly.
const obj = {};
Object.defineProperty(obj, 'name', {
    value: 'Hridoy',
    enumerable: false
});

console.log(Object.keys(obj)); // Output: [] (not enumerable)

3. configurable: Determines whether a property can be deleted or redefined.

  • If configurable: true:

    • The property can be deleted using the delete operator.

    • The property’s attributes (writable, enumerable, configurable) can be changed.

  • If configurable: false:

    • The property cannot be deleted.

    • The property’s attributes cannot be changed, except for writable (if it’s true, it can be made false).

const obj = {};
Object.defineProperty(obj, 'name', {
    value: 'Hridoy',
    configurable: false
});

delete obj.name;
console.log(obj.name); // Output: "Hridoy" (cannot delete because configurable is false)

2. How Default Descriptors Impact Property Behavior

Example 1: Creating a Property Using Object.defineProperty()

const obj = {};

Object.defineProperty(obj, 'name', {
    value: 'Hridoy',
    enumerable: true
});

console.log(obj);      // Output: { name: 'Hridoy' }
console.log(obj.name); // Output: "Hridoy"

obj.name = 'Bob';
console.log(obj.name); // Output: "Hridoy" (cannot change because writable is false)

console.log(Object.keys(obj)); // Output: [ 'name' ] (because enumerable is true)

delete obj.name;
console.log(obj.name); // Output: "Hridoy" (cannot delete because configurable is false)

Explanation:

  • writable: false (default) → obj.name = 'Bob' has no effect.

  • enumerable: true → The property appears in Object.keys(obj).

  • configurable: false (default) → delete obj.name does not work.

  • console.log(obj) does not display the property because it's non-writable and non-configurable.


3. Difference When Property is First Defined as an Object Literal

Example 2: Defining Property First as an Object Literal

const obj = { name: 'hrid' };

Object.defineProperty(obj, 'name', {
    value: 'Hridoy',
    enumerable: true
});

console.log(obj);  // Output: { name: 'Hridoy' }

obj.name = 'Bob';
console.log(obj.name); // Output: "Hridoy" (cannot change because writable is false)

console.log(Object.keys(obj)); // Output: [ 'name' ] (because enumerable is true)

delete obj.name;
console.log(obj.name); // Output: "Hridoy" (cannot delete because configurable is false)

Why Does This Behave Differently?

  • When using object literals ({ name: 'hrid' }), the property initially has:

      writable: true, enumerable: true, configurable: true
    
  • When Object.defineProperty() is used, it overwrites writable and configurable, setting them to false by default unless explicitly specified.

  • console.log(obj); displays the property.


4. How to Make a Property Editable Again?

To avoid unexpected immutability, always explicitly set writable: true and configurable: true if modification or deletion is required.

Example 3: Allowing Modification and Deletion

const obj = {};

Object.defineProperty(obj, 'name', {
    value: 'Hridoy',
    writable: true,
    enumerable: true,
    configurable: true
});

console.log(obj.name); // Output: "Hridoy"

obj.name = 'Bob';
console.log(obj.name); // Output: "Bob" (now writable)

delete obj.name;
console.log(obj.name); // Output: undefined (now configurable, so deleted successfully)

Explanation:

  • writable: true → Allows obj.name = 'Bob'.

  • configurable: true → Allows delete obj.name.


5. Key Takeaways

Default Values of Property Attributes in Object.defineProperty()

  • writable: false → Cannot modify value after initial assignment.

  • enumerable: false → Will not show up in Object.keys(obj) or loops.

  • configurable: false → Cannot delete or redefine the property.

When to Explicitly Set Attributes?

  • If you want a property to be modifiable, set writable: true.

  • If you want a property to be deletable, set configurable: true.

  • If you want a property to be included in loops, set enumerable: true.


6. Conclusion

Understanding Object.defineProperty() is essential for managing JavaScript objects precisely. The default property descriptors can lock an object property unexpectedly if not explicitly defined. Always be mindful of:

  1. Whether the property was initially created using object literals or Object.defineProperty().

  2. The default values for writable, enumerable, and configurable.

  3. Explicitly setting attributes if modification, deletion, or enumeration is required.

Next time you define an object property, consider whether you need it to be mutable or immutable and adjust your descriptors accordingly!

0
Subscribe to my newsletter

Read articles from Hridoy Chowdhury directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hridoy Chowdhury
Hridoy Chowdhury

As a sophomore pursuing a degree in Computer Science and Engineering, I am passionate about coding, problem-solving, and continuous learning. My academic journey has equipped me with a solid foundation in programming, particularly in C and C++, which I use to tackle complex problems and build efficient solutions. I am an avid LeetCode and Geeksforgeeks enthusiast, actively practicing and honing my skills in data structures and algorithms. This dedication not only helps me prepare for technical interviews but also keeps me engaged with the latest challenges in the field. Currently, I am expanding my technical repertoire by learning Web development. I believe in the power of versatile skill sets, and I am committed to broadening my knowledge and expertise in various programming languages and development tools. I am enthusiastic about collaborating on innovative projects and will contribute to open-source communities, and continually pushing the boundaries of what I can achieve in the realm of computer science.