Skip to main content

OOP

Encapsulation​

class Car {
#mileage = 0; // private field
model;
year;

constructor(model, year) {
this.model = model; // public
this.year = year; // public
}

drive(distance) {
if (distance >= 0) {
this.#mileage += distance;
return `Driven ${distance} miles, total: ${this.#mileage} miles.`;
}
}

// private
#updateMileage(amount) {
if (amount >= 0) this.#mileage += amount;
else {
// error | message | alert
}
}
}

const myCar = new Car("Toyota", 2005);
console.log(myCar.drive(150));
console.log(myCar.mileage); // SyntaxError: Private field '#mileage' must be declared in an enclosing class

Inheritance​

// parent class Person1
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

describe() {
return `${this.name}, ${this.age} years old`;
}
}

// child class Person
class Employee extends Person {
constructor(name, age, jobTitle) {
super(name, age); // call parent constructor
this.jobTitle = jobTitle;
}

describeJob() {
return `${this.name}, ${this.age} years old, ${this.jobTitle}`;
}
}

const john = new Person("John", 30);
console.log(john.describe()); // "John, 30 years old"

const jane = new Employee("Jane", 25, "Software Engineer");
console.log(jane.describe()); // "Jane, 25 years old"
console.log(jane.describeJob()); // "Jane, 25 years old, Software Engineer"

Polymorphism​

class Animal {
speak() {
return "Some sound";
}
}

Animal.prototype.greet = function () {
return "Hello";
};

let a = new Animal();
console.log(a.greet()); // Hello

class Dog extends Animal {
speak() {
return "Bark";
}
}

class Cat extends Animal {
speak() {
return "Meow";
}
}

function makeSound(animal) {
return animal.speak();
}

const dog = new Dog();
const cat = new Cat();

console.log(makeSound(dog)); // Bark
console.log(makeSound(cat)); // Meow

Example​

class Book {
constructor(title, author, year) {
this.title = title; // book title
this.author = author; // book author
this.year = year; // publication year
}

getSummary() {
return `${this.title} by ${this.author}, published in ${this.year}`;
}
}

// Bookshelf class
class Bookshelf {
constructor() {
this.books = []; // initializing an empty array to store books
}

addBook(book) {
this.books.push(book); // adding a new book to the bookshelf
}

findBooks(author) {
return this.books.filter((book) => book.author === author); // returning an array of books by the specified author
}

listBooks() {
return this.books.map((book) => book.getSummary()); // listing summaries of all books on the bookshelf
}
}

const myShelf = new Bookshelf();
myShelf.addBook(new Book("Harry Potter", "J.K. Rowling", 1997));
myShelf.addBook(new Book("The Hobbit", "J.R.R. Tolkien", 1937));
console.log(myShelf.listBooks());
console.log(myShelf.findBooks("J.R.R. Tolkien"));

Notes​

No constructor or method overloads​

You cannot define multiple constructor methods with different sets of parameters within the same class.

constructor() {
this.model = "No name";
this.year = 0;
}

constructor(model, year) {
this.model = model;
this.year = year;
}

We also can't do this with a function.

class Dog extends Animal {
speak() {
return "Bark";
}

speak(name) {
return `say my ${name}`;
}
}

JavaScript will simply overwrite the first constructor / method with the second one, and only the last declared constructor will be taken into account.

class Car {
#mileage = 0;
model;
year;

constructor(model = "No name", year = 0) {
this.model = model;
this.year = year;
}

drive(distance) {
if (distance > 0) {
this.#mileage += distance;
return `Driven ${distance} miles, total: ${this.#mileage} miles.`;
}
}

print() {
console.log(
`Hello! My name is ${this.model} from ${this.year}. My mileage is ${this.#mileage}.`,
);
}
}

const car1 = new Car();
car1.print(); // Output: Hello! My name is No name from 0. My mileage is 0.

const car2 = new Car("Tesla", 2024);
car2.print(); // Output: Hello! My name is Tesla from 2024. My mileage is 0.