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.