JavaScript Property Descriptors - Hidden Property Settings
What they are: Every object property has secret settings that control how it behaves beyond just its value.
The 3 Hidden Flags Every Property Has:
javascript
let user = { name: "John" };
// Check the hidden settings
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log(descriptor);
/*
{
value: "John",
writable: true, // Can you change the value?
enumerable: true, // Shows up in for...in loops?
configurable: true // Can you delete it or change these flags?
}
*/Default behavior: When you create properties normally, all flags are true.
writable - Can You Change the Value?
javascript
let user = { name: "John" };
// Make it read-only
Object.defineProperty(user, "name", {
writable: false
});
user.name = "Pete"; // Error! Cannot assign to read only property
console.log(user.name); // Still "John"Use case: Creating constants that can’t be accidentally changed.
enumerable - Shows Up in Loops?
javascript
let user = {
name: "John",
age: 30
};
// Hide age from loops
Object.defineProperty(user, "age", {
enumerable: false
});
for (let key in user) {
console.log(key); // Only "name" - age is hidden
}
console.log(Object.keys(user)); // ["name"] - age missing
console.log(user.age); // 30 - still accessible directlyUse case: Hide internal properties from iteration.
configurable - Can You Delete or Modify Flags?
javascript
let user = { name: "John" };
// Lock down the property
Object.defineProperty(user, "name", {
configurable: false
});
delete user.name; // Error! Cannot delete
Object.defineProperty(user, "name", { writable: false }); // Error! Cannot modify flagsUse case: Permanent protection - once set to false, you can never change it back.
Common Patterns:
Pattern 1: Create Read-Only Property
javascript
let user = {};
Object.defineProperty(user, "id", {
value: 123,
writable: false, // Can't change
enumerable: true, // Shows in loops
configurable: false // Can't delete or modify
});
user.id = 456; // Silently fails (error in strict mode)
console.log(user.id); // 123Pattern 2: Hide Internal Properties
javascript
let user = {
name: "John",
_internal: "secret"
};
// Hide the internal property
Object.defineProperty(user, "_internal", {
enumerable: false
});
for (let key in user) {
console.log(key); // Only "name"
}Pattern 3: Constants Like Math.PI
javascript
let config = {};
Object.defineProperty(config, "API_URL", {
value: "https://api.example.com",
writable: false,
enumerable: true,
configurable: false
});
// Now it's like Math.PI - completely lockedCreating Multiple Properties at Once:
javascript
let user = {};
Object.defineProperties(user, {
name: {
value: "John",
writable: true,
enumerable: true
},
age: {
value: 30,
writable: false, // Read-only age
enumerable: false // Hidden from loops
}
});Interview Gotchas:
1. Default flags for defineProperty are false:
javascript
let user = {};
// Normal way - all flags true
user.name = "John";
// defineProperty way - all flags false by default!
Object.defineProperty(user, "age", {
value: 30
// writable: false, enumerable: false, configurable: false
});2. configurable:false is permanent:
javascript
let user = { name: "John" };
Object.defineProperty(user, "name", { configurable: false });
// This is now IMPOSSIBLE:
Object.defineProperty(user, "name", { configurable: true }); // Error!
Object.defineProperty(user, "name", { enumerable: false }); // Error!
delete user.name; // Error!3. One exception - writable can go from true to false:
javascript
let user = { name: "John" };
Object.defineProperty(user, "name", { configurable: false });
// This still works:
Object.defineProperty(user, "name", { writable: false }); // OK
// But can't go back:
Object.defineProperty(user, "name", { writable: true }); // Error!Practical Uses:
1. Library constants:
javascript
const MyLibrary = {};
Object.defineProperty(MyLibrary, "VERSION", {
value: "1.0.0",
writable: false,
configurable: false
});2. Hide utility methods:
javascript
let obj = {
data: "important",
_helper() { return "internal"; }
};
Object.defineProperty(obj, "_helper", { enumerable: false });3. Clone with exact same flags:
javascript
let original = { name: "John" };
Object.defineProperty(original, "name", { writable: false });
// Copy with same flags
let clone = Object.defineProperties({},
Object.getOwnPropertyDescriptors(original)
);Quick Rules:
- writable: false = Read-only value
- enumerable: false = Hidden from loops/Object.keys
- configurable: false = Can’t delete or change flags (permanent!)
- defineProperty defaults to false for all flags
- configurable: false is one-way - can never undo it
Bottom line: Property descriptors give you fine control over how properties behave. Most useful for creating constants, hiding internals, and preventing accidental modifications.
