Lesson 57: Mastering JavaScript Property getters and setters with challenges!
✅ What Are Property Accessors?
Accessors are virtual properties in JavaScript that look like normal properties, but are backed by functions that run when you get or set them.
They let you:
Add computed logic when reading a property.
Control and validate how a property is set.
Seamlessly migrate data formats (e.g., replace
.agewith.birthdaywithout breaking existing code).
✨ Syntax in Object Literals
let user = {
firstName: "John",
lastName: "Smith",
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(value) {
[this.firstName, this.lastName] = value.split(" ");
}
};
console.log(user.fullName); // John Smith
user.fullName = "Alice Cooper";
console.log(user.firstName); // Alice
🔧 Syntax via Object.defineProperty
let user = { firstName: "John", lastName: "Smith" };
Object.defineProperty(user, 'fullName', {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
[this.firstName, this.lastName] = value.split(" ");
},
enumerable: true,
configurable: true
});
🔄 Flowchart: Accessor Execution
user.fullName ┌─────> getter() executed ─────> value returned
│
user.fullName = "x" └─────> setter(value) executed ─> side-effects or update
🔹 2. Fill Any Gaps
🚨 What Most People Miss
1. Accessors are not compatible with value/writable
You cannot mix get/set with value or writable in the same descriptor.
Object.defineProperty(obj, "x", {
get() { return 42 },
value: 10 // ❌ Error!
});
2. Only configurable descriptors can be updated
Trying to change an accessor on a non-configurable property throws an error.
3. Accessors and Prototypes
Inherited accessor properties (from prototype) behave just like data properties. The this inside the getter/setter refers to the receiver, not the prototype.
let proto = {
get name() {
return this._name?.toUpperCase();
},
set name(value) {
this._name = value;
}
};
let user = Object.create(proto);
user.name = "john";
console.log(user.name); // JOHN
4. Descriptor differences
| Descriptor Type | Has value | Has get/set | writable valid? |
| Data Property | ✅ | ❌ | ✅ |
| Accessor | ❌ | ✅ | ❌ |
⚠️ Pitfalls
Circular setter logic (setter calls itself)
Forgetting
enumerable: truewhen usingdefinePropertyUsing accessors for performance-critical code (introduces function overhead)
🔹 3. Challenge Me Deeply
🟢 Basic
Create a user object where
emailis stored in_email, but you exposeemailthrough a getter/setter that ensures it's always lowercase.Write a class
Productwith a computedpriceWithTaxgetter (base price + 18% GST).Create a
Counterobject with acountdata property, and adoublegetter that returns twice the current count.
🟡 Intermediate
Use
Object.definePropertyto create a non-enumerablesecretgetter/setter on an object.Implement an object where
fullNamegetter returnsfirstName+lastName, and setter splits and sets them.Make a
Rectangleclass whereareais a getter, andresize(newArea)method uses setter logic internally.
🔴 Advanced
Create a proxy object that intercepts setting
salaryand applies a 10% deduction tax inside a setter.Implement a class where
ageis virtual (via getter) based on a private_dobDate.Write a class with a
passwordsetter that logs a hash to console but doesn't store it.Use
Object.definePropertyto create a lazy getter (e.g.,user.profile) that fetches data only once, then caches it.
🎯 Aha! Brain-Twister
- What happens if a getter tries to access itself? Design an object where
gettercallsthis.getterinside — predict the output.
🔹 4. Interview-Ready Questions
✅ Conceptual
What are accessor properties and how are they different from data properties?
Can a property have both a getter and a value? Why not?
🔍 Scenario-Based
If
obj.nameis defined with only a getter and someone tries to assign to it, what happens?You want to prevent direct mutation of an internal
_balancevalue, but allow reading and restricted updates — how would you architect that?
🐞 Debugging
A developer reports
.nameisn’t updating in the UI. You find it’s an accessor. What might be the issue?The getter on a prototype object is returning undefined — what are the likely causes?
🚨 Best Practices & Red Flags
✅ Good:
Use
_underscorenaming convention for internal values behind getters.Keep logic inside accessors short and deterministic.
Use for backward compatibility or derived properties.
❌ Bad:
Doing async operations inside getters.
Mutating state inside getters.
Making getters non-enumerable without intention.
🔹 5. Real-World Usage
🔧 In Frameworks
Vue.js (before Composition API): reactive state uses
Object.definePropertyfor getters/setters.MobX: uses accessors to track dependencies and mutations.
Backbone.js: relies heavily on getter-style accessors for models.
ORMs: Accessors often wrap DB properties for formatting, validation, and transformation.
🌐 Production Use Cases
Format
priceto include currency symbol dynamically.Auto-calculate
isExpiredfrom atimestampfield.Prevent invalid assignments with validation logic in setters.
🔹 6. Remember Like a Pro
🧠 Mnemonic: "G-SET" — like get and set
G — Getter runs when you Get
S — Setter runs when you Set
E — Enumerable is optional
T — Two separate functions, not mixed with value
🗺️ Visual Cheatsheet
┌──────────── Accessor ─────────────┐
│ getter() ← on get │
│ setter(value) ← on assignment │
└──────────────────────────────────┘
┌──────────── Data ──────────────┐
│ value: 42 │
│ writable: true │
└───────────────────────────────┘
🔹 7. Apply It in a Fun Way
🧩 Mini Project: Smart User Profile
🔧 Build a User class with:
_dob(private birth date)age→ getter that calculates agefullName→ getter/setter that syncsfirstNameandlastNameemail→ setter that ensures lowercaseMake
profileSummary→ computed string with all info
📦 Steps
Create
Userclass with constructor forname,dob,emailAdd a
get age()that calculates fromdobAdd
get/set fullNamefor syncing name partsAdd email validation/lowercasing in
set emailAdd
get profileSummary()to print formatted data
💡 Extend it by:
Adding a
freeze()method to lock the user object (Object.freeze)Emit an event when
emailis changed
➕ Bonus Insights
🏗️ Open Source Projects
Vue 2 reactivity system
MobX dynamic observable properties
AngularJS 1.x dirty checking with
Object.defineProperty
🧨 Common Mistakes
Forgetting to define both getter and setter for computed properties.
Doing expensive or asynchronous logic inside a getter.
Confusing data and accessor descriptors.
🚀 Performance Tips
Use getters for derived data, not for logic-heavy computation.
Avoid changing accessors frequently in performance-critical paths.
🛡️ Modern Alternatives
For reactivity, prefer Proxy in modern frameworks (Vue 3, Svelte).
Use #private fields in classes to encapsulate internal values instead of
_underscoring.