|

JavaScript 32 🧬 Class getters and setters

Getters and setters are special functions used to get and set the values of an object’s properties. They allow you to control how properties are accessed and modified — providing encapsulation, validation, and computed properties.


A Quick Look at the Examples

// 1. Validation with getter/setter
class Person {
    constructor(name) {
        this._name = name;
    }

    get name() {
        return this._name;
    }

    set name(value) {
        if (typeof value === 'string') {
            this._name = value;
        } else {
            console.log('Name must be a string');
        }
    }
}

const person = new Person('Alice');
person.name = 'Bob';
console.log(person.name); // "Bob"

person.name = 123; // "Name must be a string"

// 2. Computed property with getter
class Circle {
    constructor(radius) {
        this._radius = radius;
    }

    get area() {
        return Math.PI * this._radius ** 2;
    }
}

const circle = new Circle(5);
console.log(circle.area); // 78.53981633974483

a. Getters and Setters Introduction

Getters and setters are special functions used to get and set the values of an object’s properties. They allow you to control how properties are accessed and modified — useful for encapsulation and validation.

Definitions

FeatureDescription
GetterFunction that returns the value of a property (get keyword)
SetterFunction that sets the value of a property (set keyword)

Benefits of Using Getters and Setters

BenefitDescription
EncapsulationHide the internal representation of an object’s data
ValidationEnsure only valid values are assigned to properties
Computed PropertiesDefine properties whose values are computed from other properties

Basic Syntax

class MyClass {
    constructor() {
        this._value = 0; // Internal (convention: underscore prefix)
    }

    // Getter
    get value() {
        return this._value;
    }

    // Setter
    set value(newValue) {
        this._value = newValue;
    }
}

Key points:

  • Defined with get and set keywords
  • Accessed like regular properties (no parentheses!)
  • Getters take no parameters and must return a value
  • Setters take exactly one parameter and must not return a value

Accessing Getter/Setter

const obj = new MyClass();

// Read — uses getter
console.log(obj.value); // Calls get value()

// Write — uses setter
obj.value = 42; // Calls set value(42)

Important: Getters and setters are accessed like regular properties — not like methods.

obj.value;     // ✅ Calls getter
obj.value;     // ❌ NOT obj.value()
obj.value = 5; // ✅ Calls setter

b. Class Getter

A getter is a function used to retrieve the value of a property. When you read a property, JavaScript automatically calls the associated getter if one exists.

Syntax

get propertyName() {
    // Return the value
    return this._propertyName;
}
  • Defined with the get keyword
  • Takes no parameters
  • Must return a value

Example: Computed Property

class Circle {
    constructor(radius) {
        this._radius = radius;
    }

    get area() {
        return Math.PI * this._radius ** 2;
    }
}

const circle = new Circle(5);
console.log(circle.area); // 78.53981633974483

What’s happening:

  • area is a computed property — it doesn’t store a value
  • Every time circle.area is accessed, the getter runs and computes the area
  • No parentheses needed — looks like a regular property

Visual:

circle.area
    │
    ▼
get area() {
    return Math.PI * this._radius ** 2;
}
    │
    ▼
78.53981633974483

More Getter Examples

class Rectangle {
    constructor(width, height) {
        this._width = width;
        this._height = height;
    }

    get area() {
        return this._width * this._height;
    }

    get perimeter() {
        return 2 * (this._width + this._height);
    }

    get isSquare() {
        return this._width === this._height;
    }

    get dimensions() {
        return `${this._width} × ${this._height}`;
    }
}

const rect = new Rectangle(4, 6);
console.log(rect.area);       // 24
console.log(rect.perimeter);  // 20
console.log(rect.isSquare);   // false
console.log(rect.dimensions); // "4 × 6"

Notice: None of these properties are stored — they’re computed on demand.


Getters for Read-Only Properties

Getters (without setters) create read-only properties:

class Person {
    constructor(name, birthYear) {
        this._name = name;
        this._birthYear = birthYear;
    }

    get name() {
        return this._name;
    }

    get age() {
        return new Date().getFullYear() - this._birthYear;
    }
    // No setter for age — it's computed!
}

const person = new Person('Alice', 1990);
console.log(person.name); // "Alice"
console.log(person.age);  // e.g., 34

// person.age = 25; // ❌ Silently fails (or throws in strict mode)

Best Practices for Getters

  • Use getters for accessing properties — whenever you want custom behavior when reading
  • Keep getters simple — focused on returning the property
  • Use for computed properties — when a value derives from other properties
  • Don’t make getters side-effectful — reading a property shouldn’t change state
  • Don’t do heavy computation — getters should be fast

c. Class Setter

A setter is a function used to set or modify the value of a property. When you assign a new value to a property, JavaScript automatically calls the associated setter if one exists.

Syntax

set propertyName(newValue) {
    // Modify internal state
    this._propertyName = newValue;
}
  • Defined with the set keyword
  • Takes exactly one parameter (the new value)
  • Should not return a value

Example: Validation with Setter

class Person {
    constructor(name) {
        this._name = name;
    }

    get name() {
        return this._name;
    }

    set name(value) {
        if (typeof value === 'string') {
            this._name = value;
        } else {
            console.log('Name must be a string');
        }
    }
}

const person = new Person('Alice');
person.name = 'Bob';
console.log(person.name); // "Bob"

person.name = 123; // "Name must be a string"
console.log(person.name); // "Bob" (unchanged)

What’s happening:

  • person.name = 'Bob' triggers the setter
  • Setter validates the value
  • Only valid values update _name

Visual:

person.name = 123
      │
      ▼
set name(value) {
    if (typeof value === 'string') {
        this._name = value;
    } else {
        console.log('Name must be a string'); // ← runs
    }
}

More Setter Examples

Age validation:

class User {
    constructor(age) {
        this.age = age; // Uses setter
    }

    get age() {
        return this._age;
    }

    set age(value) {
        if (!Number.isInteger(value)) {
            throw new Error('Age must be an integer');
        }
        if (value < 0 || value > 150) {
            throw new Error('Age must be between 0 and 150');
        }
        this._age = value;
    }
}

const user = new User(25);
console.log(user.age); // 25

user.age = 30;
console.log(user.age); // 30

// user.age = -5;   // ❌ Error: Age must be between 0 and 150
// user.age = 3.14; // ❌ Error: Age must be an integer

Email normalization:

class Account {
    constructor(email) {
        this.email = email; // Uses setter
    }

    get email() {
        return this._email;
    }

    set email(value) {
        if (!value.includes('@')) {
            throw new Error('Invalid email');
        }
        this._email = value.toLowerCase().trim();
    }
}

const account = new Account('  Alice@Example.COM  ');
console.log(account.email); // "alice@example.com"

Validation Best Practices

ApproachExampleUse Case
Throw errorthrow new Error('...')Strict validation — fail fast
Silently ignorereturn; or log warningLenient — don’t break on bad input
Clamp valuevalue = Math.max(0, value)Constrained numeric values
Normalizevalue.toLowerCase().trim()Clean/format input

Best Practices for Setters

  • Use setters for modifying properties — whenever you want custom behavior when writing
  • Keep setters simple — focused on validation and modification
  • Validate input — reject invalid values
  • Be consistent — if you have a getter, consider having a setter
  • Don’t make setters slow — they run on every assignment
  • Don’t forget to call this.property = value in constructor — this triggers the setter

Complete Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Class Getters and Setters</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #f8f9fa;
            color: #333;
            line-height: 1.6;
        }
        h1 { color: #007bff; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
        h2 { color: #28a745; border-left: 4px solid #28a745; padding-left: 15px; margin-top: 30px; }
        .demo-box {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            margin: 15px 0;
        }
        pre {
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 15px;
            border-radius: 8px;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
            font-size: 0.9rem;
            line-height: 1.8;
        }
        .keyword { color: #569cd6; }
        .string { color: #ce9178; }
        .number { color: #b5cea8; }
        .function { color: #dcdcaa; }
        .comment { color: #6a9955; }
        .boolean { color: #569cd6; }
        .class-name { color: #4ec9b0; }
        .private { color: #c586c0; }
        #output {
            background: #e9ecef;
            padding: 15px;
            border-radius: 8px;
            margin-top: 15px;
            min-height: 40px;
            font-family: 'Courier New', monospace;
            font-size: 0.85rem;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        th, td {
            padding: 10px;
            border: 1px solid #ddd;
            text-align: left;
        }
        th { background: #007bff; color: white; }
        tr:nth-child(even) { background: #f8f9fa; }
        .btn {
            padding: 10px 20px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            margin: 5px;
            transition: all 0.3s;
        }
        .btn:hover {
            background: #0056b3;
            transform: translateY(-2px);
        }
        .btn-success { background: #28a745; }
        .btn-success:hover { background: #1e7e34; }
        .btn-danger { background: #dc3545; }
        .btn-danger:hover { background: #a71d2a; }
        .class-display {
            font-family: 'Courier New', monospace;
            font-size: 1.05em;
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            margin: 10px 0;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
            line-height: 1.8;
        }
        .input-group {
            margin: 10px 0;
        }
        .input-group label {
            display: inline-block;
            min-width: 150px;
            font-weight: bold;
        }
        .input-group input {
            padding: 8px 12px;
            border: 2px solid #ddd;
            border-radius: 6px;
            font-size: 1em;
            width: 200px;
        }
        .input-group input:focus {
            outline: none;
            border-color: #007bff;
        }
    </style>
</head>
<body>

    <h1>Class Getters and Setters</h1>

    <div class="demo-box">
        <h2>1. Validation with Getter and Setter</h2>
        <pre>
<span class="keyword">class</span> <span class="class-name">Person</span> {
    <span class="function">constructor</span>(name) {
        <span class="keyword">this</span>._name = name;
    }

    <span class="keyword">get</span> <span class="function">name</span>() {
        <span class="keyword">return</span> <span class="keyword">this</span>._name;
    }

    <span class="keyword">set</span> <span class="function">name</span>(value) {
        <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">'string'</span>) {
            <span class="keyword">this</span>._name = value;
        } <span class="keyword">else</span> {
            console.log(<span class="string">'Name must be a string'</span>);
        }
    }
}

<span class="keyword">const</span> person = <span class="keyword">new</span> <span class="class-name">Person</span>(<span class="string">'Alice'</span>);
person.name = <span class="string">'Bob'</span>;
console.log(person.name); <span class="comment">// "Bob"</span>

person.name = <span class="number">123</span>; <span class="comment">// "Name must be a string"</span>
        </pre>
    </div>

    <div class="demo-box">
        <h2>2. Computed Property with Getter</h2>
        <pre>
<span class="keyword">class</span> <span class="class-name">Circle</span> {
    <span class="function">constructor</span>(radius) {
        <span class="keyword">this</span>._radius = radius;
    }

    <span class="keyword">get</span> <span class="function">area</span>() {
        <span class="keyword">return</span> Math.PI * <span class="keyword">this</span>._radius ** <span class="number">2</span>;
    }
}

<span class="keyword">const</span> circle = <span class="keyword">new</span> <span class="class-name">Circle</span>(<span class="number">5</span>);
console.log(circle.area); <span class="comment">// 78.53981633974483</span>
        </pre>
    </div>

    <div class="demo-box">
        <h2>3. Getter/Setter Syntax Reference</h2>
        <table>
            <tr>
                <th>Feature</th>
                <th>Syntax</th>
                <th>Notes</th>
            </tr>
            <tr>
                <td><strong>Getter</strong></td>
                <td><code>get name() { }</code></td>
                <td>No params, must return</td>
            </tr>
            <tr>
                <td><strong>Setter</strong></td>
                <td><code>set name(v) { }</code></td>
                <td>One param, no return</td>
            </tr>
            <tr>
                <td><strong>Read (getter)</strong></td>
                <td><code>obj.name</code></td>
                <td>No parentheses</td>
            </tr>
            <tr>
                <td><strong>Write (setter)</strong></td>
                <td><code>obj.name = "x"</code></td>
                <td>No parentheses</td>
            </tr>
            <tr>
                <td><strong>Read-only</strong></td>
                <td>Getter only</td>
                <td>No setter = can't write</td>
            </tr>
            <tr>
                <td><strong>Write-only</strong></td>
                <td>Setter only</td>
                <td>No getter = can't read</td>
            </tr>
        </table>
    </div>

    <div class="demo-box">
        <h2>4. Computed Properties Comparison</h2>
        <table>
            <tr>
                <th>Approach</th>
                <th>Syntax</th>
                <th>Behavior</th>
            </tr>
            <tr>
                <td><strong>Stored property</strong></td>
                <td><code>this.x = 5;</code></td>
                <td>Value stored in memory</td>
            </tr>
            <tr>
                <td><strong>Computed (getter)</strong></td>
                <td><code>get area() { }</code></td>
                <td>Computed each access</td>
            </tr>
            <tr>
                <td><strong>Method</strong></td>
                <td><code>getArea() { }</code></td>
                <td>Must call with <code>()</code></td>
            </tr>
        </table>
    </div>

    <div class="demo-box">
        <h2>5. Interactive: Person with Validation</h2>
        <div class="input-group">
            <label for="personName">Name:</label>
            <input type="text" id="personName" value="Alice">
        </div>
        <div style="margin: 10px 0;">
            <button class="btn btn-success" onclick="setName()">Set Name</button>
            <button class="btn btn-danger" onclick="setInvalidName()">Try Number (invalid)</button>
            <button class="btn" onclick="getName()">Get Name</button>
        </div>
        <div class="class-display" id="personDisplay">Person name: "Alice"</div>
    </div>

    <div class="demo-box">
        <h2>6. Interactive: Circle with Computed Area</h2>
        <div class="input-group">
            <label for="radiusInput">Radius:</label>
            <input type="number" id="radiusInput" value="5" min="0.1" step="0.1">
        </div>
        <div style="margin: 10px 0;">
            <button class="btn btn-success" onclick="updateCircle()">Update Circle</button>
            <button class="btn" onclick="showAllComputed()">Show All Properties</button>
        </div>
        <div class="class-display" id="circleDisplay">Circle with radius 5</div>
    </div>

    <div class="demo-box">
        <h2>7. Live Output — All Examples</h2>
        <div id="output">Loading...</div>
    </div>

    <script>
        // ============================================
        // Getters and Setters — Live Demo
        // ============================================

        let results = [];

        // 1. Basic getter and setter
        results.push('📌 Basic Getter and Setter:\n');

        class Person {
            constructor(name) {
                this._name = name;
            }

            get name() {
                return this._name;
            }

            set name(value) {
                if (typeof value === 'string') {
                    this._name = value;
                } else {
                    results.push('  ⚠️ Attempted to set name to ' + value + ' — must be a string');
                }
            }
        }

        const person = new Person('Alice');
        results.push('  const person = new Person("Alice")');
        results.push('  person.name → "' + person.name + '"');

        person.name = 'Bob';
        results.push('  person.name = "Bob" → person.name = "' + person.name + '"');

        person.name = 123; // Triggers validation
        results.push('  person.name = 123 → ' + person.name + ' (unchanged)');
        results.push('');

        // 2. Computed properties
        results.push('📌 Computed Properties (Getter):\n');

        class Circle {
            constructor(radius) {
                this._radius = radius;
            }

            get radius() {
                return this._radius;
            }

            set radius(value) {
                if (value <= 0) throw new Error('Radius must be positive');
                this._radius = value;
            }

            get area() {
                return Math.PI * this._radius ** 2;
            }

            get circumference() {
                return 2 * Math.PI * this._radius;
            }

            get diameter() {
                return this._radius * 2;
            }
        }

        const circle = new Circle(5);
        results.push('  const circle = new Circle(5)');
        results.push('  circle.radius → ' + circle.radius);
        results.push('  circle.diameter → ' + circle.diameter);
        results.push('  circle.area → ' + circle.area.toFixed(4));
        results.push('  circle.circumference → ' + circle.circumference.toFixed(4));
        results.push('');

        circle.radius = 10;
        results.push('  After circle.radius = 10:');
        results.push('  circle.area → ' + circle.area.toFixed(4) + ' (recomputed!)');
        results.push('');

        // 3. Read-only property (getter only)
        results.push('📌 Read-Only Property (Getter Only):\n');

        class Temperature {
            constructor(celsius) {
                this._celsius = celsius;
            }

            get celsius() {
                return this._celsius;
            }

            get fahrenheit() {
                return this._celsius * 9 / 5 + 32;
            }
        }

        const temp = new Temperature(25);
        results.push('  const temp = new Temperature(25)');
        results.push('  temp.celsius → ' + temp.celsius);
        results.push('  temp.fahrenheit → ' + temp.fahrenheit);
        results.push('  → fahrenheit is READ-ONLY (no setter)');
        results.push('');

        // 4. Full validation
        results.push('📌 Full Validation Example:\n');

        class User {
            constructor(name, age, email) {
                this.name = name;
                this.age = age;
                this.email = email;
            }

            get name() { return this._name; }
            set name(value) {
                if (typeof value !== 'string' || value.length < 2) {
                    throw new Error('Name must be at least 2 characters');
                }
                this._name = value;
            }

            get age() { return this._age; }
            set age(value) {
                if (!Number.isInteger(value) || value < 0 || value > 150) {
                    throw new Error('Age must be an integer between 0 and 150');
                }
                this._age = value;
            }

            get email() { return this._email; }
            set email(value) {
                if (!value.includes('@')) {
                    throw new Error('Invalid email');
                }
                this._email = value.toLowerCase().trim();
            }
        }

        const user = new User('Alice', 30, '  Alice@Example.COM  ');
        results.push('  const user = new User("Alice", 30, "  Alice@Example.COM  ")');
        results.push('  user.name → "' + user.name + '"');
        results.push('  user.age → ' + user.age);
        results.push('  user.email → "' + user.email + '" (normalized!)');
        results.push('');

        try {
            user.name = 'A';
        } catch (error) {
            results.push('  user.name = "A" → ❌ ' + error.message);
        }

        try {
            user.age = -5;
        } catch (error) {
            results.push('  user.age = -5 → ❌ ' + error.message);
        }

        try {
            user.email = 'invalid';
        } catch (error) {
            results.push('  user.email = "invalid" → ❌ ' + error.message);
        }
        results.push('');

        // 5. Clamping values
        results.push('📌 Clamping Values in Setter:\n');

        class Progress {
            constructor(value) {
                this.value = value;
            }

            get value() {
                return this._value;
            }

            set value(v) {
                // Clamp between 0 and 100
                this._value = Math.max(0, Math.min(100, v));
            }
        }

        const progress = new Progress(50);
        results.push('  const progress = new Progress(50)');
        results.push('  progress.value → ' + progress.value);

        progress.value = 150;
        results.push('  progress.value = 150 → clamped to ' + progress.value);

        progress.value = -10;
        results.push('  progress.value = -10 → clamped to ' + progress.value);
        results.push('');

        // 6. Property with transformation
        results.push('📌 Transformation in Setter:\n');

        class Slug {
            constructor(title) {
                this.title = title;
            }

            get title() {
                return this._title;
            }

            set title(value) {
                // Transform to slug
                this._title = value
                    .toLowerCase()
                    .trim()
                    .replace(/[^a-z0-9]+/g, '-')
                    .replace(/^-|-$/g, '');
            }
        }

        const slug = new Slug('  My First Blog Post!  ');
        results.push('  const slug = new Slug("  My First Blog Post!  ")');
        results.push('  slug.title → "' + slug.title + '"');
        results.push('');

        // 7. Getter-only with setter error
        results.push('📌 Getter-Only (Set Attempts Fail):\n');

        class Immutable {
            constructor() {
                this._value = 42;
            }

            get value() {
                return this._value;
            }
            // No setter
        }

        const imm = new Immutable();
        results.push('  const imm = new Immutable()');
        results.push('  imm.value → ' + imm.value);
        results.push('  imm.value = 100 → ❌ silently ignored (or TypeError in strict mode)');
        results.push('  imm.value → ' + imm.value + ' (unchanged)');
        results.push('');

        // 8. Practical: Bank account
        results.push('📌 Practical: Bank Account:\n');

        class BankAccount {
            #balance = 0;

            constructor(initialBalance) {
                this.balance = initialBalance;
            }

            get balance() {
                return this.#balance;
            }

            set balance(value) {
                if (value < 0) throw new Error('Balance cannot be negative');
                this.#balance = value;
            }

            deposit(amount) {
                if (amount <= 0) throw new Error('Deposit must be positive');
                this.balance = this.#balance + amount;
                return this.balance;
            }

            withdraw(amount) {
                if (amount <= 0) throw new Error('Withdrawal must be positive');
                if (amount > this.#balance) throw new Error('Insufficient funds');
                this.balance = this.#balance - amount;
                return this.balance;
            }
        }

        const account = new BankAccount(1000);
        results.push('  const account = new BankAccount(1000)');
        results.push('  account.balance → $' + account.balance);
        results.push('  account.deposit(500) → $' + account.deposit(500));
        results.push('  account.withdraw(200) → $' + account.withdraw(200));

        try {
            account.balance = -100;
        } catch (error) {
            results.push('  account.balance = -100 → ❌ ' + error.message);
        }
        results.push('');

        // 9. Practical: Config with defaults
        results.push('📌 Practical: Config with Defaults:\n');

        class Config {
            constructor(port, host) {
                this.port = port ?? 3000;
                this.host = host ?? 'localhost';
            }

            get url() {
                return `http://${this._host}:${this._port}`;
            }

            get port() { return this._port; }
            set port(value) {
                if (value < 1 || value > 65535) {
                    throw new Error('Port must be 1-65535');
                }
                this._port = value;
            }

            get host() { return this._host; }
            set host(value) {
                if (typeof value !== 'string') {
                    throw new Error('Host must be a string');
                }
                this._host = value;
            }
        }

        const config = new Config();
        results.push('  const config = new Config()');
        results.push('  config.url → "' + config.url + '"');

        const config2 = new Config(8080, 'example.com');
        results.push('  const config2 = new Config(8080, "example.com")');
        results.push('  config2.url → "' + config2.url + '"');
        results.push('');

        // 10. Getter for computed "is" checks
        results.push('📌 Practical: Boolean Getter:\n');

        class FormField {
            constructor(value) {
                this._value = value;
            }

            get value() {
                return this._value;
            }

            set value(v) {
                this._value = v;
            }

            get isEmpty() {
                return this._value.trim() === '';
            }

            get isValid() {
                return this._value.length >= 3;
            }

            get hasSpaces() {
                return /\s/.test(this._value);
            }
        }

        const field = new FormField('Hello World');
        results.push('  const field = new FormField("Hello World")');
        results.push('  field.isEmpty → ' + field.isEmpty);
        results.push('  field.isValid → ' + field.isValid);
        results.push('  field.hasSpaces → ' + field.hasSpaces);

        field.value = 'Hi';
        results.push('  After field.value = "Hi":');
        results.push('  field.isValid → ' + field.isValid);

        document.getElementById('output').textContent = results.join('\n');

        // ============================================
        // Interactive: Person with Validation
        // ============================================

        const interactivePerson = new Person('Alice');

        function setName() {
            const newName = document.getElementById('personName').value;
            interactivePerson.name = newName;
            document.getElementById('personDisplay').textContent =
                `Person name: "${interactivePerson.name}"\n` +
                `\n` +
                `Setting name to: "${newName}"\n` +
                `Result: ✅ Success`;
        }

        function setInvalidName() {
            const oldName = interactivePerson.name;
            interactivePerson.name = 12345;
            document.getElementById('personDisplay').textContent =
                `Person name: "${interactivePerson.name}"\n` +
                `\n` +
                `Attempted to set name to: 12345 (number)\n` +
                `Result: ⚠️ Rejected — name unchanged`;
        }

        function getName() {
            document.getElementById('personDisplay').textContent =
                `Person name: "${interactivePerson.name}"\n` +
                `\n` +
                `Reading via getter: person.name\n` +
                `Result: "${interactivePerson.name}"`;
        }

        // ============================================
        // Interactive: Circle
        // ============================================

        let interactiveCircle = new Circle(5);

        function updateCircle() {
            const radius = Number(document.getElementById('radiusInput').value);
            try {
                interactiveCircle.radius = radius;
                document.getElementById('circleDisplay').textContent =
                    `Circle with radius ${interactiveCircle.radius}\n` +
                    `─────────────────────────────\n` +
                    `radius:        ${interactiveCircle.radius}\n` +
                    `diameter:      ${interactiveCircle.diameter}\n` +
                    `area:          ${interactiveCircle.area.toFixed(4)}\n` +
                    `circumference: ${interactiveCircle.circumference.toFixed(4)}`;
            } catch (error) {
                document.getElementById('circleDisplay').textContent =
                    `❌ Error: ${error.message}`;
            }
        }

        function showAllComputed() {
            document.getElementById('circleDisplay').textContent =
                `Circle Properties (all computed):\n` +
                `─────────────────────────────\n` +
                `radius:        ${interactiveCircle.radius}\n` +
                `diameter:      ${interactiveCircle.diameter} (= 2 × radius)\n` +
                `area:          ${interactiveCircle.area.toFixed(4)} (= π × r²)\n` +
                `circumference: ${interactiveCircle.circumference.toFixed(4)} (= 2π × r)\n` +
                `\n` +
                `→ None of these are stored — computed on demand!`;
        }

        // Initialize
        updateCircle();
    </script>

</body>
</html>

Quick Reference

Getter and Setter Syntax

FeatureSyntaxRules
Getterget name() { }No params, must return
Setterset name(v) { }One param, no return
Readobj.nameNo parentheses
Writeobj.name = valueNo parentheses

Benefits

BenefitDescription
EncapsulationHide internal representation
ValidationEnsure only valid values are assigned
Computed PropertiesDefine values computed from others
Clean SyntaxAccess like regular properties

When to Use Getters

ScenarioUse Getter?
Computed value✅ Yes
Read-only property✅ Yes
Formatted output✅ Yes
Validation on read⚠️ Rarely
Side effects❌ No
Heavy computation❌ No

When to Use Setters

ScenarioUse Setter?
Validation✅ Yes
Normalization✅ Yes
Clamping values✅ Yes
Transformation✅ Yes
Side effects⚠️ Cautiously
Heavy computation❌ No

Best Practices

Do This:

// Getter for computed values
class Rectangle {
    get area() {
        return this._width * this._height;
    }
}

// Setter for validation
class User {
    set age(value) {
        if (value < 0) throw new Error('Age cannot be negative');
        this._age = value;
    }
}

// Use underscore for internal property
class Person {
    constructor(name) {
        this._name = name; // Internal
    }
    get name() { return this._name; }
    set name(v) { this._name = v; }
}

// Use private fields for true encapsulation
class BankAccount {
    #balance = 0;
    get balance() { return this.#balance; }
    set balance(v) {
        if (v < 0) throw new Error('Invalid balance');
        this.#balance = v;
    }
}

// Call setters in constructor
class Product {
    constructor(price) {
        this.price = price; // Uses setter for validation
    }
    set price(value) {
        if (value < 0) throw new Error('Price must be positive');
        this._price = value;
    }
}

Don’t Do This:

// Don't use same name for internal and getter
class Bad {
    constructor(name) {
        this.name = name; // ❌ Infinite loop!
    }
    get name() { return this.name; } // Recursion!
}

// Don't forget to return in getter
get name() {
    this._name; // ❌ Missing return!
}

// Don't return in setter
set name(value) {
    return this._name = value; // ❌ Setters shouldn't return
}

// Don't use getter/setter for performance-critical code
class BadPerf {
    get expensiveValue() {
        return doHeavyComputation(); // ❌ Runs on every access
    }
}

// Don't forget validation in setters
set age(value) {
    this._age = value; // ❌ No validation — defeats the purpose!
}

// Don't access #private fields outside class
class BadPrivate {
    #value = 42;
}
const obj = new BadPrivate();
// console.log(obj.#value); // ❌ SyntaxError

Common Pitfalls

PitfallProblemSolution
Same name as propertyInfinite recursionUse _ prefix or #
Missing return in getterReturns undefinedAlways return
Returning in setterUnexpected behaviorDon’t return
Forgetting validationNo benefit from setterAdd validation logic
Using # outside classSyntaxErrorOnly inside class body
Heavy computation in getterPerformance issuesCache or use method

Getter vs Method

class Rectangle {
    constructor(w, h) {
        this._w = w;
        this._h = h;
    }

    // Getter — accessed like property
    get area() {
        return this._w * this._h;
    }

    // Method — called with parentheses
    calculateArea() {
        return this._w * this._h;
    }
}

const rect = new Rectangle(4, 6);

rect.area;           // 24 (getter — no parens)
rect.calculateArea(); // 24 (method — with parens)
rect.area();         // ❌ TypeError: area is not a function

When to use which:

Use Getter WhenUse Method When
Value derives from propertiesAction/operation
No parameters neededParameters required
Fast computationHeavy computation
Accessed like a propertyClearly an operation

Pro Tip: Getters and setters are powerful tools for encapsulation and validation. Use them to:

  • Compute properties on the fly (get area())
  • Validate input (set age(value))
  • Normalize data (set email(v) { this._email = v.toLowerCase(); })
  • Create read-only properties (getter only)
  • Create write-only properties (setter only)

Key rules:

  • Getters take no parameters and must return
  • Setters take one parameter and should not return
  • Use _ prefix for internal properties (convention)
  • Use # prefix for truly private fields
  • Call setters in constructor to ensure validation runs
  • Never use the same name for the internal property and the getter — infinite recursion!

Remember: Getters and setters provide a clean API — consumers use obj.prop instead of obj.getProp(). But don’t overuse them! For simple data, plain properties work fine. Use getters/setters when you need validation, computation, or encapsulation.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!