Object-Oriented Programming With Classes in JavaScript
Object-Oriented Programming With Classes in JavaScript
~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
With class, JavaScript builds a familiar, object-oriented syntax on top of its actual prototype system (chapter 20). Let's now model an Account that manages our budget app in a structured way.
The first class: Account
export class Account {
constructor(name, startingBalance) {
this.name = name;
this.balance = startingBalance;
this.transactions = [];
}
record(description, amount, category) {
this.balance += amount;
this.transactions.push({ description, amount, category });
}
formatBalance() {
return `${this.name}: ${this.balance.toFixed(2)} EUR`;
}
}import { Account } from './account.js';
const account = new Account('Checking', 1500);
account.record('January salary', 2400, 'Salary');
account.record('January rent', -850, 'Rent');
console.log(account.formatBalance()); // 'Checking: 3050.00 EUR'
console.log(account.transactions.length); // 2constructor runs automatically on EVERY new Account(...) call and initializes the instance properties. Methods like record() are SHARED across ALL instances of the class (via the prototype, chapter 20) – not recreated for every new instance.
Getters and setters
Computed properties that LOOK like regular properties but are actually methods:
class Account {
constructor(name, startingBalance) {
this.name = name;
this._balance = startingBalance; // convention: '_' signals 'don't use directly'
}
get balance() {
return this._balance;
}
set balance(newValue) {
if (newValue < 0) {
throw new Error('Balance cannot be set to a negative value');
}
this._balance = newValue;
}
}
const account = new Account('Checking', 1500);
console.log(account.balance); // 1500 - reads like a normal property, but calls get()
account.balance = 2000; // writes like a normal property, but calls set()
account.balance = -50; // Error: Balance cannot be set to a negative valueReal private fields with #
Since ES2022, JavaScript supports REAL privacy at the language level – unlike the _ convention above (which only expresses a STYLISTIC intent, but is still technically accessible from outside), a field marked with # is truly INACCESSIBLE from outside the class:
class Account {
#balance; // real private field
constructor(name, startingBalance) {
this.name = name;
this.#balance = startingBalance;
}
get balance() {
return this.#balance;
}
}
const account = new Account('Checking', 1500);
console.log(account.balance); // 1500 - via the getter
console.log(account.#balance); // SyntaxError - directly impossible from outside!Tipp: This is the modern, language-native alternative to the closure-based "private state" from chapter 16 – for classes, # is the preferred approach today.
Static methods and properties
static members belong to the CLASS itself, not to a single instance – useful for helper functions that logically belong to the class but don't need access to a specific instance:
class Account {
static CURRENCY = 'EUR';
static createEmptyAccount(name) {
return new Account(name, 0);
}
constructor(name, startingBalance) {
this.name = name;
this.balance = startingBalance;
}
}
console.log(Account.CURRENCY); // 'EUR' - accessed via the CLASS, not an instance
const newAccount = Account.createEmptyAccount('Savings');
console.log(newAccount.balance); // 0class vs. object literal: when which?
| Tool | When it fits |
|---|---|
| Object literal (chapter 12) | For SINGLE, one-off data structures with no behavior or little shared logic – e.g. a single transaction. |
| class | When SEVERAL similar instances are needed with shared behavior (methods) and encapsulated, validated state – e.g. multiple accounts with booking logic. |