Skip to main content

Command Palette

Search for a command to run...

Object-Oriented Programming in JavaScript for Beginners

Updated
•9 min read•View as Markdown
Object-Oriented Programming in JavaScript for Beginners
K
Trying to build more for future

In this article we're going to review the main characteristics of object oriented programming (OOP) with practical JavaScript examples.

A) What is Object-Oriented Programming?

OOP is very useful on large scale projects, as it facilitates code modularity and organization. By implementing the abstraction of entities, we're able to think about the program in a similar way as our world works, with different actors that perform certain actions and interact with each other.


B) What is a class in JavaScript?

In JavaScript, a class is a blueprint or a template for creating new objects with predefined properties and methods, introduced in ECMAScript 2015 (ES6). Classes encapsulate data and methods, providing a cleaner, more structured way to implement object-oriented programming (OOP) patterns. They are primarily syntactical sugar over JavaScript's existing prototype-based inheritance model.


C) Creating objects using classes

As we know that class provide a template or blueprint for creating new objects. But can you imagine how class do that? and if template is same for all class then how it create objects with different properties and method? Let's understand step by step, first understand how objects create in normal method.

Let's create a car game with different types of car. We define some cars and all car has certain characteristics (properties) like color, engine type, name, and so on and abilities (methods) like speed, acceleration, loads, and so on. Objects are the perfect data structure to use to store this kind of information.

We have 3 different car "type" available and we want to create 6 different car, 2 of each type.

const sportsCar1 = {
  name: "Mustang",
  type: "sports car",
  start: () => console.log("Mustang engine started!"),
  drift: () => console.log("Mustang is drifting!")
}

const sportsCar2 = {
  name: "Supra",
  type: "sports car",
  start: () => console.log("Supra engine started!"),
  drift: () => console.log("Supra is drifting!")
}
                   ///////////////////////////////////
const truck1 = {
  name: "Cybertruck",
  type: "truck",
  start: () => console.log("Cybertruck engine started!"),
  load: () => console.log("Cybertruck is carrying heavy load!")
}

const truck2 = {
  name: "F150",
  type: "truck",
  start: () => console.log("F150 engine started!"),
  load: () => console.log("F150 is carrying heavy load!")
}
                    ///////////////////////////////////
const electricCar1 = {
  name: "Model S",
  type: "electric car",
  start: () => console.log("Model S powered on!"),
  autopilot: () => console.log("Autopilot activated!")
}

const electricCar2 = {
  name: "Ioniq 5",
  type: "electric car",
  start: () => console.log("Ioniq 5 powered on!"),
  autopilot: () => console.log("Autopilot activated!")
}

See that all car has the name and type properties and also the start method. Moreover, each species has a method that belongs only to that species (for example, "sportsCar1" have the drift method).

As you can see, some data is shared by all car, some data is shared by each car type, and some data is unique to each car.

This approach works. See that we can perfectly access properties and methods like this:

console.log(sportsCar1.name) // output: "Mustang"

console.log(truck2.type) // output: "truck"

electricCar1.start() // output: "Model S powered on!"

electricCar2.autopilot() // output: "Autopilot activated!"

Here is problem comes. The problem with this is that it doesn't scale well at all. Imagine that our game could have hundreds of cars. We would need to manually set the properties and methods for each of them! To solve this problem we need a programmatic solution of creating objects and setting different properties and methods given a set of conditions. And that's what classes are good for. And you already know what class does.

class SportsCar {
  constructor(name, message) {
    this.name = name
    this.message = message
    this.type = "sports car"
  }
  drift = () => console.log("Screeeeeech!! Drifting...")
  sayMessage = () => console.log(this.message)
}

class Truck {
  constructor(name, message) {
    this.name = name
    this.message = message
    this.type = "truck"
  }

  load = () => console.log("Loading heavy cargo!")
  sayMessage = () => console.log(this.message)
}

class ElectricCar {
  constructor(name, message) {
    this.name = name
    this.message = message
    this.type = "electric car"
  }

  autopilot = () => console.log("Autopilot mode activated!")
  sayMessage = () => console.log(this.message)
}

We can instantiate our characters from those classes:

const sportsCar1 = new SportsCar("Mustang", "Built for speed!")
const sportsCar2 = new SportsCar("Supra", "Let's dominate the race!")

const truck1 = new Truck("Cybertruck", "I can carry anything!")
const truck2 = new Truck("F150", "Strong and reliable!")

const electricCar1 = new ElectricCar("Model S", "Silent but powerful!")
const electricCar2 = new ElectricCar("Ioniq 5", "Future of driving!")

We can access each object properties and methods:

console.log(sportsCar1.name) // output: "Mustang"

console.log(truck2.type) // output: "truck"

electricCar1.sayMessage() // output: "Silent but powerful!"

electricCar2.autopilot() // output: "Autopilot mode activated!"

D) What is Constructor ?

In JavaScript, a constructor function inside a class is a special method that runs automatically when a new object is created from that class and assign initial values to an object's properties.


E) The Four Principles of OOP

OOP is normally explained with 4 key principles that define how OOP programs work. These are Inheritance, Encapsulation, Abstraction and Polymorphism. Let's see each of them.

1) Inheritance

Inheritance is the ability to create classes based on other classes. With inheritance, we can define a parent class (with certain properties and methods) and then children classes that will inherit from the parent class all the properties.

class Truck {
  constructor(name, message, power) {
    this.name = name
    this.message = message
    this.power = power
    this.type = "truck"
  }

  load = () => console.log("Carrying heavy cargo!")

  sayMessage = () => console.log(this.message)

  boost = () => console.log(`Boosting with a power of ${this.power}!`)
}

class SportsCar {
  constructor(name, message, power) {
    this.name = name
    this.message = message
    this.power = power
    this.type = "sports car"
  }

  drift = () => console.log("Screeeeeech!! Drifting...")

  sayMessage = () => console.log(this.message)

  boost = () => console.log(`Boosting with a power of ${this.power}!`)
}

const truck1 = new Truck("Cybertruck", "Built for heavy work!", 10)
const sportsCar1 = new SportsCar("Mustang", "Built for speed!", 15)

console.log(truck1.power) 
// output: 10

sportsCar1.boost()
// output: "Boosting with a power of 15!"

A better way would be to declare a parent "Vehicle" class

class Vehicle {
  constructor(power) {
    this.power = power
  }

  boost = () => console.log(`This vehicle boosts with a power of ${this.power}!`)
}

class SportsCar extends Vehicle {
  constructor(name, message, power) {
    super(power)
    this.name = name
    this.message = message
    this.type = "sports car"
  }

  drift = () => console.log("Screeeeeech!! Drifting...")

  sayMessage = () => console.log(this.message)
}

The constructor method receives parameters and assigns them as properties on the object. When creating a child class, we use the extends keyword to specify which parent class it inherit from. Inside the child's constructor, any parameters that belong to the parent class must be passed up using the super() function — this tells JavaScript to initialize those properties in the parent before setting up the child's own.

If we don't pass the parameter inside super() then no property inherit from parent to child. super() only take parent's parameter not method that parent has.

const sportsCar1 = new SportsCar("Mustang", "Built for speed!", 120)
const sportsCar2 = new SportsCar("Supra", "Ready to win the race!", 150)

sportsCar1.boost() // output: This vehicle boosts with a power of 120!

console.log(sportsCar2.power) // output: 150

2) Encapsulation

Encapsulation is another key concept in OOP and it stands for an object's capacity to "decide" which information it exposes to "the outside" and which it doesn't. Encapsulation is implemented through public and private properties and methods. "public" means that property or method, you can access outside of class, but for "private", you can not.

We first need to declare the private property, always using the '#' symbol as the start of its name.

class SportsCar extends Vehicle {
  #manufactureYear // private property 

  constructor(name, message, power, speed, manufactureYear) {
    super(power, speed)
    this.name = name
    this.message = message
    this.type = "sports car"
    this.#manufactureYear = manufactureYear 
  }

  drift = () => console.log("Screeeeeech!! Drifting...")

  carYear = () => console.log(`This car was manufactured in ${this.#manufactureYear}`)
}


const car1 = new SportsCar("Mustang", "Built for speed!", 120, 200, 2022)

If you try to access it, it shows error :

car1.carYear();
// output: This car was manufactured in 2022

console.log(car1.#manufactureYear);
// ❌ Error: Private field '#manufactureYear' must be declared in an enclosing class

If you log the objects, it doesn't show the private property:

console.log(car1);

/*
SportsCar {
  name: 'Mustang',
  message: 'Built for speed!',
  power: 120,
  speed: 200,
  type: 'sports car',
  drift: [Function],
  carYear: [Function]
}
*/

3) Abstraction

Abstraction is 3rd principle that says that a class should only represent information that is relevant to the problem. Only expose to the outside the properties and methods that you're going to use. If it's not needed, don't expose it. Kind of same as Encapsulation, but not fully.

class Car {
  constructor(brand) {
    this.brand = brand
  }
 
  #igniteEngine() {
    console.log("Engine ignition process running...")
  }

  start() {
    this.#igniteEngine()
    console.log(`${this.brand} is ready to drive!`)
  }

  stop() {
    console.log(`${this.brand} has stopped.`)
  }

  
}

const car1 = new Car("Tesla")

car1.start()
// Engine ignition process running...
// Tesla is ready to drive!

car1.stop()
// Tesla has stopped.

The driver only interacts with simple methods, while the internal logic stays hidden inside the class.


4) Polymorphism

Polymorphism means in JavaScript "many output objects" and is actually a simple concept. It's the ability of one method to return different values according to conditions. There are two kind of Polymorphism,

  • Parameter Based Polymorphism.

When the same method behaves differently depending on the values passed to it.

class Car {
  constructor(name, speed) {
    this.name = name
    this.speed = speed
  }

  drive() {
    console.log(`\({this.name} is driving at \){this.speed} km/h`)
  }
}

const car1 = new Car("Mustang", 120)
const car2 = new Car("Tesla Model S", 200)

car1.drive()
// Mustang is driving at 120 km/h

car2.drive()
// Tesla Model S is driving at 200 km/h
  • Inheritance Based Polymorphism.

This happens when a child class overrides a method from a parent class.

class Car {
  start() {
    console.log("Car engine started")
  }
}
class ElectricCar extends Car {
  start() {
    console.log("Electric car powered on silently")
  }
}

class SportsCar extends Car {
  start() {
    console.log("Sports car engine roars loudly!")
  }
}

const car1 = new ElectricCar()
const car2 = new SportsCar()

car1.start()
// Electric car powered on silently

car2.start()
// Sports car engine roars loudly!

Final Thought

OOP is a very powerful programming method that can help us tackle huge projects by creating the abstraction of entities. Each entity will be responsible for certain information and actions and entities will be able to interact with each other too, much like how the real world works.