Lesson 57: Mastering JavaScript Property getters and setters with challenges!

manoj ymkmanoj ymk
6 min read

βœ… 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 .age with .birthday without 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 TypeHas valueHas get/setwritable valid?
Data Propertyβœ…βŒβœ…
AccessorβŒβœ…βŒ

⚠️ Pitfalls

  • Circular setter logic (setter calls itself)

  • Forgetting enumerable: true when using defineProperty

  • Using accessors for performance-critical code (introduces function overhead)


πŸ”Ή 3. Challenge Me Deeply

🟒 Basic

  1. Create a user object where email is stored in _email, but you expose email through a getter/setter that ensures it's always lowercase.

  2. Write a class Product with a computed priceWithTax getter (base price + 18% GST).

  3. Create a Counter object with a count data property, and a double getter that returns twice the current count.

🟑 Intermediate

  1. Use Object.defineProperty to create a non-enumerable secret getter/setter on an object.

  2. Implement an object where fullName getter returns firstName + lastName, and setter splits and sets them.

  3. Make a Rectangle class where area is a getter, and resize(newArea) method uses setter logic internally.

πŸ”΄ Advanced

  1. Create a proxy object that intercepts setting salary and applies a 10% deduction tax inside a setter.

  2. Implement a class where age is virtual (via getter) based on a private _dob Date.

  3. Write a class with a password setter that logs a hash to console but doesn't store it.

  4. Use Object.defineProperty to create a lazy getter (e.g., user.profile) that fetches data only once, then caches it.

🎯 Aha! Brain-Twister

  1. What happens if a getter tries to access itself? Design an object where getter calls this.getter inside β€” 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.name is defined with only a getter and someone tries to assign to it, what happens?

  • You want to prevent direct mutation of an internal _balance value, but allow reading and restricted updates β€” how would you architect that?

🐞 Debugging

  • A developer reports .name isn’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 _underscore naming 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.defineProperty for 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 price to include currency symbol dynamically.

  • Auto-calculate isExpired from a timestamp field.

  • 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 age

  • fullName β†’ getter/setter that syncs firstName and lastName

  • email β†’ setter that ensures lowercase

  • Make profileSummary β†’ computed string with all info


πŸ“¦ Steps

  1. Create User class with constructor for name, dob, email

  2. Add a get age() that calculates from dob

  3. Add get/set fullName for syncing name parts

  4. Add email validation/lowercasing in set email

  5. Add get profileSummary() to print formatted data

πŸ’‘ Extend it by:

  • Adding a freeze() method to lock the user object (Object.freeze)

  • Emit an event when email is 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.

0
Subscribe to my newsletter

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

Written by

manoj ymk
manoj ymk