Lesson 127 min read

Classes & OOP

Build blueprints for objects — then stamp out as many as you need

What Are Classes?

Imagine you're running a pet shop. Each pet has a name, a type, an age, and can do things like eat or sleep. You could write a separate object for each pet by hand, but that gets old fast. A class is like a cookie cutter — you define the shape once, then stamp out as many cookies (objects) as you want.

Each object created from a class is called an instance. The class defines what an instance looks like and what it can do. The constructor method runs automatically when you create a new instance with new.

Creating a Class

class Pet {
constructor(name, type, age) {
this.name = name;
this.type = type;
this.age = age;
this.energy = 100;
}
// Method
eat(food) {
this.energy += 20;
console.log(`${this.name} eats ${food}. Energy: ${this.energy}`);
}
sleep() {
this.energy += 50;
console.log(`${this.name} takes a nap. Energy: ${this.energy}`);
}
info() {
return `${this.name} is a ${this.age}-year-old ${this.type}`;
}
}
// Create instances with 'new'
const cat = new Pet("Mochi", "cat", 3);
const dog = new Pet("Pixel", "dog", 5);
console.log(cat.info());
cat.eat("tuna");
dog.sleep();
console.log(cat instanceof Pet); // true
Output
Mochi is a 3-year-old cat
Mochi eats tuna. Energy: 120
Pixel takes a nap. Energy: 150
true

Getters, Setters & Static Methods

Getters and setters let you define properties that look like regular data but actually run code behind the scenes. A getter computes a value when you read a property. A setter runs validation or logic when you set a property.

Static methods belong to the class itself, not to instances. You call them on the class name directly (like Math.random()). They're useful for utility functions related to the class.

Getters, Setters & Static

class Temperature {
#celsius; // private field (starts with #)
constructor(celsius) {
this.#celsius = celsius;
}
// Getter — access like a property, runs like a function
get fahrenheit() {
return this.#celsius * 9/5 + 32;
}
// Setter — validates when you assign
set celsius(value) {
if (value < -273.15) {
throw new RangeError("Below absolute zero!");
}
this.#celsius = value;
}
get celsius() {
return this.#celsius;
}
// Static method — called on the class, not instances
static fromFahrenheit(f) {
return new Temperature((f - 32) * 5/9);
}
}
const water = new Temperature(100);
console.log(water.fahrenheit); // 212 (getter, looks like a property)
console.log(water.celsius); // 100
water.celsius = 0; // setter runs validation
console.log(water.fahrenheit); // 32
// Static method — called on the CLASS
const body = Temperature.fromFahrenheit(98.6);
console.log(body.celsius.toFixed(1)); // 37.0
Output
212
100
32
37.0

Inheritance — extends & super

Inheritance lets you create a specialized version of an existing class. It's like saying "a Dog is a Pet, but with extra tricks." The child class inherits all properties and methods from the parent, and can add new ones or override existing ones.

Use extends to create a child class and super to call the parent's constructor or methods.

Inheritance in Action

class Character {
constructor(name, health) {
this.name = name;
this.health = health;
}
describe() {
return `${this.name} (HP: ${this.health})`;
}
takeDamage(amount) {
this.health -= amount;
console.log(`${this.name} takes ${amount} damage! HP: ${this.health}`);
}
}
class Warrior extends Character {
constructor(name, health, weapon) {
super(name, health); // call parent constructor FIRST
this.weapon = weapon;
}
attack(target) {
let damage = Math.floor(Math.random() * 20) + 10;
console.log(`${this.name} swings ${this.weapon}!`);
target.takeDamage(damage);
}
}
class Mage extends Character {
constructor(name, health, mana) {
super(name, health);
this.mana = mana;
}
// Override parent method
describe() {
return `${super.describe()} | Mana: ${this.mana}`;
}
castSpell(target) {
if (this.mana < 10) {
console.log(`${this.name} is out of mana!`);
return;
}
this.mana -= 10;
target.takeDamage(30);
console.log(`${this.name} casts fireball! Mana left: ${this.mana}`);
}
}
const knight = new Warrior("Roland", 100, "greatsword");
const wizard = new Mage("Elara", 70, 50);
console.log(knight.describe());
console.log(wizard.describe());
wizard.castSpell(knight);
Output
Roland (HP: 100)
Elara (HP: 70) | Mana: 50
Roland takes 30 damage! HP: 70
Elara casts fireball! Mana left: 40
Note: Classes in JavaScript are really just "syntactic sugar" over the older prototype-based system. Under the hood, there are no traditional classes like in Java or C++ — it's still prototypes all the way down. But the class syntax is much cleaner and easier to read, so use it!

Quick check

What does the 'constructor' method do in a class?
Error HandlingModules & Imports