Dmitrii Korolkov

github | email

JavaScript Object-Oriented Programming: Getters, Setters, and Composition

JavaScript supports multiple OOP patterns including class-based inheritance, composition, getters/setters, and more. Below are the most widely used patterns with examples.


Getters and Setters

Getters and setters allow controlled access to object properties. Useful for validation, computed properties, or encapsulation.


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

    // Getter: read property
    get name() {
        return this._name;
    }

    // Setter: validate or modify before assignment
    set age(value) {
        if (value > 0) this._age = value;
        else console.log("Age must be positive");
    }
}

const p = new Person("Dmitrii", 28);
console.log(p.name); // Dmitrii
p.age = 30;           // Valid
p.age = -5;           // Age must be positive

Class-Based Inheritance

Inheritance allows one class to extend another, reusing and overriding behavior. Great for hierarchical models like Employees extending Person.


class Employee extends Person {
    constructor(name, age, role) {
        super(name, age);
        this.role = role;
    }

    work() {
        console.log(this.name + " is working as " + this.role);
    }
}

const e = new Employee("Dmitrii", 28, "Developer");
e.work(); // Dmitrii is working as Developer

Object Composition (Mixins)

Composition lets you combine behaviors from multiple objects. Ideal for flexible feature sharing without deep inheritance chains.


const canEat = { eat() { console.log('Eating'); } };
const canWalk = { walk() { console.log('Walking'); } };

const person = { ...canEat, ...canWalk };
person.eat();  // Eating
person.walk(); // Walking

Private Fields & Static Methods

Private fields hide internal data from outside access. Static methods belong to the class itself, not instances. Useful for utility methods and encapsulation.


class Account {
    #balance = 0; // private field

    constructor(owner) {
        this.owner = owner;
    }

    deposit(amount) {
        this.#balance += amount;
        console.log(this.owner + " balance: " + this.#balance);
    }

    static info() {
        console.log("Accounts handle deposits and withdrawals");
    }
}

const acc = new Account("Dmitrii");
acc.deposit(100);   // Dmitrii balance: 100
// acc.#balance;     // Error: private field
Account.info();     // Accounts handle deposits and withdrawals

Method Chaining / Fluent API

Method chaining allows multiple operations in a single statement. Useful for builders, configurations, and fluent APIs.


class Builder {
    constructor() { this.result = ""; }

    add(text) {
        this.result += text;
        return this; // return this to allow chaining
    }

    print() {
        console.log(this.result);
        return this; // optional chaining
    }
}

const b = new Builder();
b.add("Hello ").add("World!").print().add(" Again!").print();
// Output:
// Hello World!
// Hello World! Again!