Topic 31 of 59 JavaScript Asynchronous & Advanced Concepts Interactive Tutorial

JavaScript Getters and Setters: Controlled Access to Object Properties

23 mins read Last updated: 22 Aug 2026 14453 views
Quick Summary & Key Takeaway Target Skill Level: Beginner to Advanced ES6+

Bottom-Line Answer: JavaScript (JS) is a high-level, single-threaded, just-in-time (JIT) compiled programming language with first-class functions that conforms to the ECMAScript specification. It powers dynamic client-side web interactivity alongside HTML and CSS, and executes backend server runtimes via Node.js, Deno, and Bun.

Target Audience: Frontend developers, full-stack engineers, students & QA testers.
Prerequisites: Basic HTML semantics & fundamental CSS styling rules.
Primary Use Cases: Dynamic web UI, DOM manipulation, asynchronous REST APIs, Node.js backend services.

What is JavaScript Getters and Setters? Core Concept & Direct Summary

Core Objective & Problem Statement

How do getter and setter methods work on JavaScript objects and classes?

Direct Answer Summary

Here's a problem hiding in plain sight. Fromour bank account looked safe:

script.js
Run Online
class BankAccount {
  constructor(owner, balance) {
    this.owner = owner;
    this.balance = balance;
  }
}const account = new BankAccount("Priya", 1000);

But watch this disaster:

Regular properties areopen doors— anyone can stuff any garbage into them. Your carefully designed object has zero defense against invalid data.

Getters and settersare the fix: they look like normal properties on the outside, but secretly runfunctionson the inside — letting you validate, transform, compute, and protect. The outside world seesaccount.balance; behind the scenes, your code stands guard. 🛡️

In this lesson:getandsetsyntax, validation gates, computed properties, and how they pair with private#fieldsfrom Lesson 23. Let's build some smart doors.

How Does JavaScript Getters and Setters Work? In-Depth Mechanics & Architecture

What Are Getters and Setters?

  • Getter— a function that runs automatically when a property isread📖
  • Setter— a function that runs automatically when a property iswritten✏️

The magic trick: theylook like properties but act like functions.

script.js
Run Online
const user = {
  firstName: "Priya", lastName: "Sharma", // 📖 GETTER — runs when you READ fullName:  get fullName() {    return `${this.firstName} ${this.lastName}`;  }};
  // Looks like a property… no parentheses!console.log(user.fullName);    // "Priya Sharma" — the function ran! ✨

Notice:user.fullName—no(). It reads like data, but a function executed behind the scenes. That's the entire superpower.


The Syntax: get and set in Objects

script.js
Run Online
const user = {
  firstName: "Priya", lastName: "Sharma", // 📖 Called on READ:  get fullName() {    return `${this.firstName} ${this.lastName}`;  },
  // ✏️ Called on WRITE — receives the assigned value:  set fullName(value) {    const parts = value.split(" ");    this.firstName = parts[0];    this.lastName = parts[1];  }};
  // Reading → the GETTER runs:console.log(user.fullName);     // "Priya Sharma"
  // Writing → the SETTER runs:user.fullName = "Amit Verma";   // setter splits and stores!
  console.log(user.firstName);
  // "Amit" — updated automatically ✅console.log(user.lastName);     // "Verma" ✅console.log(user.fullName);     // "Amit Verma" — getter reflects changes

One property name (fullName), two behaviors— reading and writing, each with custom logic. The outside world just saysuser.fullName = "…"and has no idea a function ran. Beautiful encapsulation.


The Killer Use #1: Validation Gates 🛡️

Now let's fix that bank account disaster from the intro:

script.js
Run Online
class BankAccount {
  #balance = 0;
  // 🔒 private field (Lesson 23!)
  constructor(owner, balance) {
    this.owner = owner;
    this.balance = balance;
    // goes THROUGH the setter — validated!  }
    // 📖 Read access:  get balance() {    return this.#balance;  }
    // ✏️ Write access — WITH A GUARD:  set balance(amount) {    if (typeof amount !== "number" || isNaN(amount)) {      console.log("❌ Balance must be a number!");      return;                        // rejected — door stays shut    }    if (amount < 0) {      console.log("❌ Balance can't be negative!");      return;    }    this.#balance = amount;          // ✅ passed inspection  }}
    const account = new BankAccount("Priya", 1000);
    console.log(account.balance);
    // 1000 ✅
    account.balance = 500;
    // ✅ valid — acceptedconsole.log(account.balance);      // 500
    account.balance = -99999;
    // ❌ "Balance can't be negative!"account.balance = "hello";         // ❌ "Balance must be a number!"console.log(account.balance);      // still 500 — protected! 🛡️
    console.log(account.#balance);
    // ❌ SyntaxError — truly private!

The architecture:

  • #balance— the real data, locked in a private vault 🔒
  • get balance— the controlled reading window 📖
  • set balance— the security checkpoint ✏️

Invalid data now physicallycannotenter your object. This is encapsulation (OOP Pillar #1 from Lesson 23) fully realized.

⚠️ Why the Private Field Is Essential Here

Watch what happenswithoutit — the infinite loop trap:

script.js
Run Online
// 💀 BROKEN — infinite recursion:class BadAccount {
set balance(amount) {
  this.balance = amount;
  // 😱 this CALLS the setter again → again → again…
}// 💥 RangeError: Maximum call stack size exceeded
}

this.balance = amountinside theset balancesetter… triggersset balanceagain. Forever.The rule:getters/setters must store data in adifferentproperty — a#privatefield or a_conventionalname:

script.js
Run Online
set balance(amount) {
  this.#balance = amount;
  // ✅ different name — no recursion
}// (older style you may see: this._balance = amount; — same idea, convention only)

The Killer Use #2: Computed Properties 🧮

Getters are perfect for values that should always stayin sync— compute them on demand instead of storing stale copies:

script.js
Run Online
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  } // Area is always correct — calculated fresh every read:  get area() {    return this.width * this.height;  }}
  const box = new Rectangle(10, 5);
  console.log(box.area);
  // 50
  box.width = 20;
  // change the input…console.log(box.area);     // 100 — area updated automatically! ✨

The alternative without a getter— a storedareaproperty — goes stale the momentwidthchanges. The getter canneverbe stale because it computes live. Real-world examples:fullNamefrom first/last,totalfromprice × qty,agefrombirthYear.

Temperature Converter — Two-Way Computed 🌡️

Getters and setters can even translate between units transparently:

script.js
Run Online
class Temperature {
  #celsius = 0;
  get celsius() {
    return this.#celsius;
  } set celsius(value) {
    this.#celsius = value;
  } get fahrenheit() {
    return (this.#celsius * 9 / 5) + 32;
    // compute on read  }
    set fahrenheit(value) {
      this.#celsius =(value - 32) * 5 / 9;
      // convert on write!  }}
      const temp = new Temperature();
      temp.celsius = 25;
      console.log(temp.fahrenheit);
      // 77 — translated automatically! ✅
      temp.fahrenheit = 212;
      console.log(temp.celsius);
      // 100 — reverse translation! ✅

The class storesonetruth (Celsius) but speakstwolanguages. Users of your class never see the conversion math — abstraction (Pillar #4!) in action.


Getters/Setters in Plain Objects vs Classes

Same keywords, both homes:

script.js
Run Online
// 🏠 Plain object literal:const product = {  _price: 499,                          // _ = "private by convention"
get price() {
  return `₹${this._price}`;
}, set price(value) {
  if (value > 0) this._price = value;
}
};
// 🏭 Class (from Lesson 23):class Product {  #price = 0;                           // # = truly private
get price() {
  return `₹${this.#price}`;
} set price(value) {
  if (value > 0) this.#price = value;
}
}

Both work identically on the outside. Use objects for one-offs, classes when manufacturing many instances.


Read-Only and Write-Only Properties 🔐

Omit the setter→ read-only property:

script.js
Run Online
class Circle {
  constructor(radius) {
    this.radius = radius;
  } get area() {
    return (Math.PI * this.radius ** 2).toFixed(2);
  } // no setter — nobody can assign circle.area!}
  const c = new Circle(7);
  console.log(c.area);
  // "153.94" ✅c.area = 999;            // silently ignored (or TypeError in strict mode)console.log(c.area);     // still "153.94" — protected! 🛡️

Omit the getter→ write-only (rare, but legal — e.g., a password field you can set but never read back).


Getters/Setters vs Plain Methods: Which Style?

script.js
Run Online
// Style A — getter:get fullName() { return `${this.firstName} ${this.lastName}`; }user.fullName;                       // reads like data
// Style B — method:getFullName() { return `${this.firstName} ${this.lastName}`; }user.getFullName();                  // reads like an action
Use a Getter When…Use a Method When…
The value feels likedata(name, area, price)The operation feels like anaction(save, send, calculate)
It's cheap to computeIt's expensive or has side effects
No arguments neededYou need to pass arguments (getters can't take any!)

🏆 Rule of thumb:if it answers"what IS it?"→ getter. If it answers"what does it DO?"→ method. And remember: getters/setters takezero parameters— need input? That's a method's job.


🛠️ Mini Project: Smart User Profile 👤

script.js
Run Online
// 👤 User Profile — validation + computed properties + privacy
class UserProfile {
  #email = "";
  #age = 0;
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  } // 📖 Computed: always in sync
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  } // ✏️ Validated email (regex from Lesson 28! 🔍):
  get email() {
    return this.#email;
  } set email(value) {
    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailPattern.test(value)) {
      console.log("❌ Invalid email address!");
      return;
    } this.#email = value.toLowerCase();
    // normalized on the way in ✨
  } // ✏️ Validated age with guard rails:
  get age() {
    return this.#age;
  } set age(value) {
    if (!Number.isInteger(value) || value < 0 || value > 120) {
      console.log("❌ Age must be a whole number between 0 and 120!");
      return;
    } this.#age = value;
  } // 📖 Computed from age:
  get canVote() {
    return this.#age >= 18;
  }
}// Test drive:
const user = new UserProfile("Priya", "Sharma");
user.email = "PRIYA@Example.COM";
console.log(user.email);
// "priya@example.com" — normalized! ✅
user.email = "not-an-email";
// ❌ Invalid email address!
user.age = 22;
console.log(user.canVote);
// true ✅
user.age = -5;
// ❌ Age must be a whole number…
user.age = "twenty";
// ❌ rejected too
console.log(user.fullName);
// "Priya Sharma"

Output:

script.js
Run Online
priya@example.com❌ Invalid email address!true❌ Age must be a whole number between 0 and 120!❌ Age must be a whole number between 0 and 120!Priya Sharma

Every property has a bouncer. The outside world uses simple property syntax; the class enforces all the rules.This is professional-grade object design.🏆


Common Beginner Mistakes (And Fixes)

❌ Mistake🔍 What Happens✅ Fix
Setter assigns to its own name (this.x = …insideset x)Infinite recursion → stack overflow 💥Store in#xor_x
Calling a getter with()—user.fullName()TypeError: not a functionAccess like data:user.fullName
Passing arguments to a setterImpossible — setters take exactly one (the value)Use a method for multi-input logic
Getter with side effects (fetching, logging storms)Confusing, unpredictable readsGetters should be cheap and pure
Expecting_privateconvention to protect dataIt's fully accessible 😬Use#privatefor real privacy
Getters for expensive computations on every accessPerformance dragCompute once, cache, or use a method

Quick Self-Check: Lesson 31 Quiz 🧠

  1. What runs when youreada property defined withget? When youwriteto one defined withset?
  2. Why does this crash?
    script.js
    Run Online
    class Demo {
      set value(v) {
        this.value = v;
      }
    }
  3. How do you create a read-only property with getters/setters?
  4. What's the output?
    script.js
    Run Online
    const square = {
      side: 4, get area() {
        return this.side ** 2;
      }
    };
    square.side = 5;
    console.log(square.area);
  5. When should you choose a method over a getter?

(Answers: 1. The getter function runs on read; the setter function runs on write, receiving the assigned value · 2. Infinite recursion — the setter keeps calling itself; store in this.#value instead · 3. Define only a getter, no setter — writes are then ignored/error · 4. 25 — the getter computes live from the updated side · 5. When the operation needs arguments, is expensive, or performs an action — getters are for cheap, data-like reads)

JavaScript Decision Matrix & Syntax Comparison

Understanding the mechanical differences between variable declarations and execution environments is critical for writing performant, bug-free modern JavaScript.

Variable Declarations: var vs let vs const
Feature / Propertyvar (Legacy ES5)let (Modern ES6+)const (Modern ES6+)
ScopeFunction ScopeBlock Scope ({})Block Scope ({})
Hoisting BehaviorHoisted (initialized as undefined)Hoisted (Temporal Dead Zone - ReferenceError)Hoisted (Temporal Dead Zone - ReferenceError)
Reassignable✓ Yes✓ Yes✗ No (Immutable binding)
Redeclarable in Same Scope✓ Yes (Error-prone)✗ No (SyntaxError)✗ No (SyntaxError)
Window Object BindingCreates property on windowDoes not attach to windowDoes not attach to window
Environment Decision Matrix: Browser Client-Side vs Node.js Server-Side
DimensionClient-Side (Web Browser)Server-Side (Node.js / Deno)
Global Objectwindow, document, navigatorglobal, process, globalThis
DOM & UI Manipulation✓ Full access (document.querySelector)✗ No DOM (Headless / Non-UI)
File System & OS Access✗ Blocked (Sandboxed security)✓ Full access (fs, os, child_process)
Primary FocusInteractive UI, animations, client stateREST APIs, microservices, databases, I/O streaming

Runnable JavaScript Code Example & Practical Implementation

script.js
Run Online
const user = {
  firstName: 'Jane', lastName: 'Doe', get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }, set fullName(name) {
[this.firstName, this.lastName] = name.split(' ');
  }
};
console.log(user.fullName);
// 'Jane Doe'
user.fullName = 'John Smith';
console.log(user.firstName);
// 'John'

Frequently Asked Questions & Technical Interview Insights

Technical Interview & Viva Questions

Frequently asked questions for frontend & full-stack roles
Q

Q: Can getter properties be reassigned if no setter is provided?

A: In non-strict mode, reassigning a getter-only property fails silently; in strict mode, it throws a TypeError.

Related Tools, Tutorials & Authoritative References

Recommended Interactive Tools

Authoritative Standards & Citations