- Write a class with a
constructorand methods and create instances withnew - Extend a class with
extendsandsuperand override a method - Use
#private fields, getters/setters andstaticmethods
A bank has thousands of customers, and every account has the same structure: an owner, a balance, and operations to deposit and withdraw money. Writing each account object by hand is slow and error-prone. A class is a blueprint for objects: you describe the structure once and then create as many objects from it as you like.
Class, constructor and methods
A class is declared with the keyword class, and its name starts with a capital letter. The special constructor method is called automatically whenever a new object is created, and it sets the object's properties. this refers to the specific object being created. Objects are created with the new operator; an object made from a class is called an instance.
class BankAccount {
constructor(owner, balance = 0) {
this.owner = owner;
this.balance = balance;
}
deposit(amount) {
this.balance += amount;
return this.balance;
}
describe() {
return `${this.owner}: ${this.balance} AZN`;
}
}
const aysel = new BankAccount('Aysel', 100);
const murad = new BankAccount('Murad');
aysel.deposit(50);
console.log(aysel.describe());
console.log(murad.describe());
console.log(aysel instanceof BankAccount);▸ Expected output
Aysel: 150 AZN Murad: 0 AZN true
Inheritance: extends and super
A savings account can do everything a normal account can, and it also earns interest. To avoid repeating code, we derive the new class from the old one with extends: it inherits all the parent's methods. In the child's constructor you must call super(...) first — it runs the parent's constructor. A method with the same name overrides the parent's method, while super.describe() calls the parent's version.
class BankAccount {
constructor(owner, balance = 0) {
this.owner = owner;
this.balance = balance;
}
deposit(amount) { this.balance += amount; }
describe() { return `${this.owner}: ${this.balance} AZN`; }
}
class SavingsAccount extends BankAccount {
constructor(owner, balance, rate) {
super(owner, balance);
this.rate = rate;
}
addInterest() { this.deposit((this.balance * this.rate) / 100); }
describe() { return `${super.describe()} (savings, ${this.rate}%)`; }
}
const leyla = new SavingsAccount('Leyla', 1000, 5);
leyla.addInterest();
console.log(leyla.describe());
console.log(leyla instanceof BankAccount);▸ Expected output
Leyla: 1050 AZN (savings, 5%) true
addInterest() once a year, how would her 1000 manat grow in 10 years? Change the rate and the period.Private fields, getters and static methods
- A field starting with
#is private: it can be reached only from inside the class. Writinghome.#celsiusoutside is a syntax error. getandsetmethods are read and written like properties, but code runs behind them — for example, to check a value.- A
staticmethod belongs to the class itself, not to an instance, and is called like this:Thermostat.fromFahrenheit(68).
class Thermostat {
#celsius = 20;
get celsius() { return this.#celsius; }
set celsius(value) {
if (value < 5 || value > 30) {
throw new RangeError('Temperature must be between 5 and 30');
}
this.#celsius = value;
}
get fahrenheit() { return (this.#celsius * 9) / 5 + 32; }
static fromFahrenheit(f) {
const t = new Thermostat();
t.celsius = Math.round(((f - 32) * 5) / 9);
return t;
}
}
const home = new Thermostat();
home.celsius = 25;
console.log(home.celsius, home.fahrenheit);
try { home.celsius = 40; } catch (error) { console.log(error.name, '-', error.message); }
console.log(Thermostat.fromFahrenheit(68).celsius);▸ Expected output
25 77 RangeError - Temperature must be between 5 and 30 20
Your own error class
A very useful application of inheritance is creating your own kinds of errors. When you extend the built-in Error class and give it a name, you can identify the kind of error precisely in a catch block with instanceof, which makes it easy to separate expected errors from unexpected ones.
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
function register(age) {
if (age < 13) {
throw new ValidationError('Too young to register');
}
return 'Welcome!';
}
for (const age of [16, 10]) {
try {
console.log(register(age));
} catch (error) {
if (error instanceof ValidationError) {
console.log(`${error.name}: ${error.message}`);
} else {
throw error;
}
}
}▸ Expected output
Welcome! ValidationError: Too young to register
| Term | Meaning |
|---|---|
class | a blueprint for objects |
new | creates a new instance from a class |
constructor | sets up the new instance's properties |
extends / super | inheritance and calling the parent |
#field | a field visible only inside the class |
static | a method of the class, not of an instance |
Complete the Rectangle class: the constructor stores width and height, and area() returns the area. Then write Square, which extends Rectangle: its constructor takes a single side and calls super.
class Rectangle {
constructor(width, height) {
// save width and height
}
area() {
// return the area
}
}
class Square extends Rectangle {
// constructor(side) → call super
}
const rect = new Rectangle(3, 4);
const square = new Square(5);
console.log(rect.area(), square.area());
console.log(square instanceof Rectangle);
console.log(square);▸ Expected output
12 25
true
Square { width: 5, height: 5 }Key points
- A class is a blueprint for objects; instances are created with
new. - The
constructorsets the properties, andthispoints to the specific instance. extendsgives inheritance; a child constructor must callsuper()first.#fields are private,get/setcontrol a property,staticbelongs to the class itself.- You can create your own kinds of errors by extending the
Errorclass.
Check yourself
10 questions. Every correct answer earns XP.