When you create an object property in JavaScript, it comes with hidden metadata.
These metadata are called Property Descriptors.
They control how a property behaves:
Can you change its value?
Can you delete it?
Is it visible during looping?
Does it have getter/setter?
🔥 Types of Property Descriptors
Data Property Descriptor → describes a normal property storing a value
Accessor Property Descriptor → describes getter/setter based property
🧩 1. Data Property Descriptor Fields
Every normal property has these three descriptor fields:
Field | Default | Meaning |
|---|---|---|
value | the actual value | Stores the value |
writable |
| Can the value be changed? |
enumerable |
| Will it show in loops like |
configurable |
| Can we delete or change descriptors? |
✔️ Example
const user = {
name: "Bhaskar"
};
console.log(Object.getOwnPropertyDescriptor(user, "name"));{
value: "Bhaskar",
writable: true,
enumerable: true,
configurable: true
}🧩 2. Accessor Property Descriptor Fields
Used when a property has get or set:
Field | Meaning |
|---|---|
get | function that returns value |
set | function that sets value |
enumerable | loop |
configurable | delete or change |
❗ Note: Accessor descriptors do NOT use value or writable.
✔️ Example of Accessor Property
const user = {
firstName: "Bhaskar",
lastName: "Das",
get fullName() {
return this.firstName + " " + this.lastName;
},
set fullName(value) {
const [f, l] = value.split(" ");
this.firstName = f;
this.lastName = l;
}
};
console.log(user.fullName); // Bhaskar Das
user.fullName = "Amit Singh";
console.log(user.firstName); // Amit
console.log(user.lastName); // Singh
console.log(Object.getOwnPropertyDescriptor(user, "fullName"));{
enumerable: true,
configurable: true,
get: ƒ fullName()
length: 0
name: "get fullName"
set: ƒ fullName(value)
length: 1
name: "set fullName"
}🛠️ Object.defineProperty()
Used to manually control descriptors.
Example: make a property read-only
const user = {};
Object.defineProperty(user, "name", {
value: "Bhaskar",
writable: false, // ❌ cannot change
enumerable: true,
configurable: true
});
user.name = "Das"; // ignored
console.log(user.name); // BhaskarExample: Hide functions/methods from loops
const user = {
name: "Bhaskar",
age: 25
};
// Add method manually and hide it from loops
Object.defineProperty(user, "sayHi", {
value: function () {
console.log("Hi!");
},
enumerable: false // 🔥 method will NOT appear in loops
});
console.log(Object.keys(user));
for (let key in user) {
console.log(" ->", key);
}[ 'name', 'age' ]
-> name
-> age
// When enumerable: true
['name', 'age', 'sayHi']
-> name
-> age
-> sayHi⚠️ Important Notes
✔️ 1. If you use defineProperty(), unspecified descriptors default to:
writable: falseenumerable: falseconfigurable: false
✔️ 2. Accessor and Data descriptors cannot be mixed:
// ❌ invalid
Object.defineProperty({}, "x", {
value: 10,
get() {
return 20;
}
});🧠 Why Property Descriptors Are Useful?
To create read-only properties
To hide properties from loops (
enumerable: false)To lock API objects (
configurable: false)To create libraries/frameworks (React, Vue, etc. use them internally)
To control getters/setters