Object Properties Configuration
Property Flags and Descriptors
Object properties have more than just a value. Each property has three flags:
- writable β can the value be changed?
- enumerable β does it show up in
for...inloops? - configurable β can the property be deleted or modified?
Property Descriptor
let user = { name: "Alice" };
let descriptor = Object.getOwnPropertyDescriptor(user, "name");
console.log(descriptor);
// β { value: "Alice", writable: true, enumerable: true, configurable: true }
Defining Properties with Flags
let user = {};
Object.defineProperty(user, "name", {
value: "Alice",
writable: false, // Can't change
enumerable: false, // Won't appear in loops
configurable: false // Can't delete or reconfigure
});
user.name = "Bob"; // Fails silently (in strict mode: TypeError)
console.log(user.name); // β "Alice"
for (let key in user) console.log(key); // Nothing β not enumerable
delete user.name; // Fails silently
console.log(user.name); // β "Alice"
Non-writable
let user = {};
Object.defineProperty(user, "id", {
value: 42,
writable: false // Read-only
});
user.id = 100; // Silently ignored (or TypeError in strict)
console.log(user.id); // β 42
Non-enumerable
let user = {
name: "Alice",
age: 30
};
// Make a property hidden from loops
Object.defineProperty(user, "internalId", {
value: "SECRET",
enumerable: false
});
for (let key in user) console.log(key);
// β "name", "age" (internalId is hidden)
console.log(Object.keys(user)); // β ["name", "age"]
console.log(user.internalId); // β "SECRET" (still accessible directly)
Non-configurable
A non-configurable property canβt be deleted or redefined:
let user = {};
Object.defineProperty(user, "name", {
value: "Alice",
configurable: false
});
delete user.name; // Silently ignored
// Object.defineProperty(user, "name", { writable: false }); // TypeError!
Object.defineProperties (multiple at once)
let user = {};
Object.defineProperties(user, {
name: { value: "Alice", writable: true, enumerable: true },
age: { value: 30, writable: false, enumerable: true },
id: { value: 42, writable: false, configurable: false }
});
Object.getOwnPropertyDescriptors
Get all property descriptors at once:
let user = { name: "Alice", age: 30 };
let descriptors = Object.getOwnPropertyDescriptors(user);
console.log(descriptors);
// β {
// name: { value: "Alice", writable: true, enumerable: true, configurable: true },
// age: { value: 30, writable: true, enumerable: true, configurable: true }
// }
Sealing and Freezing Objects
let user = { name: "Alice", age: 30 };
// Seal β prevent adding/removing properties (sets configurable: false)
Object.seal(user);
delete user.age; // Silently ignored
user.email = "test"; // Silently ignored (can't add)
user.name = "Bob"; // β
Allowed (writable)
// Freeze β make completely immutable
Object.freeze(user);
user.name = "Charlie"; // Silently ignored
delete user.age; // Silently ignored
// Check state
Object.isSealed(user); // β true
Object.isFrozen(user); // β true
Object.isExtensible(user); // β false
| Method | Add | Delete | Write | Reconfigure |
|---|---|---|---|---|
seal() | β | β | β | β |
freeze() | β | β | β | β |
preventExtensions() | β | β | β | β |
Property Getters and Setters
Accessor properties are functions that look like regular properties.
Getter and Setter Syntax
let user = {
firstName: "Alice",
lastName: "Johnson",
// Getter
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
// Setter
set fullName(value) {
[this.firstName, this.lastName] = value.split(" ");
}
};
console.log(user.fullName); // β "Alice Johnson"
user.fullName = "Bob Smith";
console.log(user.firstName); // β "Bob"
console.log(user.lastName); // β "Smith"
Why use getters/setters?
- Computed properties β derive a value from other properties
- Validation β check values before setting
- Encapsulation β hide internal representation
Validation with Setter
let user = {
_age: 0, // _ prefix by convention means "internal"
get age() {
return this._age;
},
set age(value) {
if (value < 0 || value > 150) {
throw new Error("Invalid age");
}
this._age = value;
}
};
user.age = 25;
console.log(user.age); // β 25
// user.age = -5; // β Error: Invalid age
Using defineProperty for accessors
let user = { firstName: "Alice", lastName: "Johnson" };
Object.defineProperty(user, "fullName", {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
[this.firstName, this.lastName] = value.split(" ");
},
enumerable: true,
configurable: true
});
console.log(user.fullName); // β "Alice Johnson"
Getter/Setter Descriptors
Accessor descriptors donβt have value or writable:
let descriptor = Object.getOwnPropertyDescriptor(user, "fullName");
console.log(descriptor);
// β { get: [Function], set: [Function], enumerable: true, configurable: true }
Smart Getter β Lazy Computation
let data = {
_cache: null,
get bigData() {
if (!this._cache) {
console.log("Computing...");
this._cache = heavyComputation();
}
return this._cache;
}
};
console.log(data.bigData); // β "Computing..." then result
console.log(data.bigData); // β From cache (no computation)
Compatibility with Getters/Setters
Getters/setters let you add logic without breaking existing code:
// Before β simple property
let user = { name: "Alice" };
// After β refactored to getter (code using user.name still works)
let user = {
_name: "Alice",
get name() { return this._name; },
set name(value) {
if (value.length < 2) throw new Error("Name too short");
this._name = value;
}
};
Demo: Getters and Setters Demo
JavaScript
let user = {
_name: 'Alice',
_age: 30,
get name() {
return this._name;
},
set name(value) {
if (!value) throw new Error('Name required');
this._name = value;
},
get age() {
return this._age;
},
set age(value) {
if (value < 0 || value > 150) throw new Error('Invalid age');
this._age = value;
},
get isAdult() {
return this._age >= 18;
},
get description() {
return this._name + ' is ' + this._age + ' years old';
}
};
document.write('Name: ' + user.name + '<br>');
document.write('Age: ' + user.age + '<br>');
document.write('Adult: ' + user.isAdult + '<br>');
document.write('Description: ' + user.description + '<br>');
// Try setting
user.name = 'Bob';
user.age = 25;
document.write('<br>After update:<br>');
document.write('Name: ' + user.name + '<br>');
document.write('Description: ' + user.description + '<br>'); Live Output Window