All You Need to Know About JavaScript Object-Oriented Programming (OOP)
Tags: webdev, javascript, programming, beginners
Published: 2023-08-11 11:09:13

In JavaScript, Object-Oriented Programming (OOP) is a fundamental concept that is used to organize your code into reusable and structured components. By breaking down your code into structured components, you can enhance its readability and maintainability. In this blog post, we'll cover the concepts of Object-Oriented Programming (OOP).

Let's start!

## What is Object-Oriented Programming (OOP)?
Object-Oriented Programming is a programming paradigm (a way of writing code) that centers on the idea of "objects". These objects can be thought of as building blocks to create things.

In OOP, you create these objects based on a blueprint known as a "class". It's like making many cars using the same car blueprint. Each car shares the same design principles, structure, and features defined in the blueprint, but they can still have their own unique characteristics, such as color or accessories.

OOP helps you keep your code organized and makes it easier to understand how different parts of your program interact with each other.

## Classes and Constructors
A class is a blueprint for creating objects with similar properties and methods. Constructors are functions used to create and initialize objects based on that class's blueprint.

For example:

```
class Car {
  constructor(company, model) {
    this.company = company;
    this.model = model;
  }
}
```
In the above example, `Car` is our blueprint and the `constructor(company, model)` is like a set of instructions we follow to create a new car with the given "company" and "model."

In other words, a `class` is like a plan for creating objects, and the `constructor` is the set of instructions that every new object follows to get its initial details.

## Creating Objects
Once we have a class, we can create objects(instances) using the `new` keyword.

`const carObject = new Car('Ford', 'Mustang');`

## Prototype
The prototype is a property that allows objects to take properties and methods from other objects.

Every object in JavaScript has a `prototype` property. When we try to access a property or method on an object, JavaScript first checks if that property or method exists on the object itself. If it doesn't, JavaScript looks up the prototype object. If the prototype doesn't have it, JavaScript keeps looking at "parent" prototypes until it finds the property or method.

To put it simply, think of it as borrowing things from a friend. If you don't have something, you ask your friend. If your friend doesn't have it, they might ask their friend, and this can continue until someone has what you're looking for.

For example:

```
//class and constructor
class Car {
  constructor(company, model) {
    this.company = company;
    this.model = model;
  }
}

//Adding a method to the prototype of Car
Car.prototype.hello = function() {
    console.log(`Hello, I'm ${this.model} model of ${this.company} company!`);
};

// Creating objects using the Car constructor
const carObject1 = new Car('Ford', 'Mustang');
const carObject2 = new Car('Toyota', 'Fortuner');

carObject1.hello(); // Output: Hello, I'm Mustang model of Ford company!
carObject2.hello(); // Output: Hello, I'm Fortuner model of Toyota company!
```
In the above example, the `hello` method is defined in the `Car.prototype`. When we create objects using `new Car()`, they automatically inherit the `hello` method from the prototype.

In other words, when `carObject1.hello()` is called, JavaScript first checks if `hello` exists on the `carObject1`, and if it doesn't, it looks in the `Car.prototype`.

You can also add methods inside the class. For example:

```
//class and constructor
class Car {
  constructor(company, model) {
    this.company = company;
    this.model = model;
  }
  hello() { //Adding a method to the prototype of Car
    console.log(`Hello, I'm ${this.model} model of ${this.company} company!`);
  }
}

// Creating objects using the Car constructor
const carObject1 = new Car('Ford', 'Mustang');
const carObject2 = new Car('Toyota', 'Fortuner');

carObject1.hello(); // Output: Hello, I'm Mustang model of Ford company!
carObject2.hello(); // Output: Hello, I'm Fortuner model of Toyota company!
```

## `this` keyword
The `this` keyword refers to the current object or instance that a method is being called on.

It represents the object that a function or method belongs to and helps to access the object's properties and methods from within its functions.

```
class Person {
  constructor(name) {
    this.name = name;
  }

  hello() {
    console.log(`Hello, I'm ${this.name}!`);
  }
}

const person1 = new Person('John');
const person2 = new Person('Mark');

person1.hello(); // Output: Hello, I'm John!
person2.hello(); // Output: Hello, I'm Mark!
```

## Class Inheritance
Class inheritance is a method of one class to inherit properties and methods from another class. This can be done using the `extends` keyword.

For example, we want to add a year property to our `Car` blueprint.

```
//parent class
class Car {
  constructor(company, model) {
    this.company = company;
    this.model = model;
  }
}
//child class
class carYear extends Car{
  constructor(company, model, year) {
    super(company, model); // Call the constructor of the parent class
    this.year = year;
  }
}
// Creating object using the carYear constructor
const carObject = new carYear('Ford', 'Mustang', 1969);

console.log(carObject.company + " " + carObject.model + " " + carObject.year); 
//Output: Ford Mustang 1969
```
Calling the constructor of the parent class into the child class using the `super` keyword is known as constructor overriding.

## `super` keyword
The `super` keyword is used to call and access methods or properties from a parent class within a child class.

## Method Overriding
Method overriding allows you to change or replace a method's behavior in a child class while keeping the same method name as the parent class.

For example:

```
//parent class
class Car {
  constructor(company, model) {
    this.company = company;
    this.model = model;
  }
}
//Adding a method to the prototype of Car
Car.prototype.hello = function() {
    console.log(`Hello, I'm ${this.model} model of ${this.company} company!`);
};

//child class
class carYear extends Car{
  constructor(company, model, year) {
    super(company, model); // Call the constructor of the parent class
    this.year = year;
  }
}
//Adding a method to the prototype of carYear
carYear.prototype.hello = function() {
    console.log(`Hello, I'm ${this.year} ${this.model} model of ${this.company} company!`);
};

// Creating object using the Car constructor
const carObject = new Car('Ford', 'Mustang');

// Creating object using the carYear constructor
const carYearObject = new carYear('Ford', 'Mustang', 1969);

carObject.hello(); //Hello, I'm Mustang model of Ford company!
carYearObject.hello(); //Hello, I'm 1969 Mustang model of Ford company!
```
In the above example, we have a `Car` class with a method `hello()`and a `carYear` class that extends Car. In the `carYear` class, we override the `hello()` method. When we create an object of the `carYear` class and call the `hello()` method, it uses the overridden method from the `carYear` class, not the one from the `Car` class. This is method overriding.

## Static Methods
Static methods are methods that are associated with a class rather than with objects of the class. They are called on the class itself, not on objects of the class. Static methods are defined using the `static` keyword within a class declaration.

```
class Car {
  constructor(company, model) {
    this.company = company;
    this.model = model;
  }
  start() { 
  console.log(`${this.model} of ${this.company}  is starting...`);
  }
  static info() {
  console.log("This is a Car class.");
 }
}

// Creating object of the Car class
const carObject = new Car('Ford', 'Mustang');

// Calling object methods 
carObject.start(); // Mustang of Ford  is starting...

// Calling static method
Car.info(); // This is a Car class.
```

## Getters and Setters
Getters and setters are special methods that allow us to define how properties of an object are accessed (getter) and modified (setter).

For example:

```
class Car {
  constructor(company, model) {
    this._company = company;
    this._model = model;
  }

  // Getter for the company property
  get company() {
    return this._company;
  }

  // Setter for the company property
  set company(newCompany) {
    if (typeof newCompany === 'string') {
      this._company = newCompany;
    } else {
      console.log("company must be a string");
    }
  }

  // Getter for the model property
  get model() {
    return this._model;
  }

  // Setter for the model property
  set model(newModel) {
    if (typeof newModel === 'string') {
      this._model = newModel;
    } else {
      console.log("model must be a string");
    }
  }
};

// Creating an object of the Car class
const carObject = new Car('Ford', 'Mustang');

// Using getter for company
console.log(`Company: ${carObject.company}`); //Output- Company: Ford

// Using setter for company
carObject.company = 'Toyota'; 
console.log(`Company: ${carObject.company}`); //Output- Company: Toyota

// Using getter for model
console.log(`Model: ${carObject.model}`); //Output- Model: Mustang

// Using setter for model
carObject.model = 'Fortuner'; 
console.log(`Model: ${carObject.model}`); //Output- Model: Fortuner

//Try to set invalid value
carObject.model = 3; //Output- model must be a string
```
In the above example, the `Car` class has getter and setter methods for the `company` and `model` properties. The getters allow you to retrieve the values of these properties, while the setters provide a way to validate and set new values for the properties.

Here, the underscores `_company` and `_model` are used to indicate that these properties are meant to be private to the `Car` class and should not be accessed or modified directly from outside the class.

## `instanceof` Operator
The `instanceof` operator is used to check whether an object is an instance of a particular class or constructor function.

For example:

```
//parent class
class Car {
  constructor(company, model) {
    this.company = company;
    this.model = model;
  }
}

//child class
class carYear extends Car{
  constructor(company, model, year) {
    super(company, model); 
    this.year = year;
  }
}

// Creating object using the Car constructor
const carObject = new Car('Ford', 'Mustang');

// Creating object using the carYear constructor
const carYearObject = new carYear('Ford', 'Mustang', 1969);

//Using instanceof operator

console.log(carObject instanceof Car); // true
console.log(carObject instanceof carYear); // false

console.log(carYearObject instanceof Car); // true
console.log(carYearObject instanceof carYear); // true
```
In the above example, we have a `Car` class and a `carYear` class that inherits from `Car`. We create instances(objects) of both classes and then use the `instanceof` operator.

`carObject` is an instance of `Car`, but it's not an instance of `carYearObject`. `carYearObject` is an instance of both `Car` and `carYear`.

## Conclusion
In JavaScript, Object-Oriented Programming (OOP) is a paradigm that revolves around the concept of objects, which are created based on class blueprints.

Key concepts covered in this blog post include:

- Classes and Constructors: A class is a blueprint for creating objects with similar properties and methods. Constructors are functions used to create and initialize objects based on that class's blueprint.

- Prototype: The prototype is a property that allows objects to take properties and methods from other objects.

- 'this' Keyword: 'this' refers to the current object or instance and is used to access its properties and methods within functions or methods.

- Class Inheritance: The 'extends' keyword enables one class to inherit properties and methods from another class.

- Super Keyword: 'super' is used to call and access methods or properties from a parent class within a child class.

- Method Overriding: Child classes can override parent class methods to modify behavior while keeping the same method name.

- Static Methods: Static methods belong to the class itself and are called on the class, not on object instances.

- Getters and Setters: Getters retrieve property values, and setters validate and set new property values, allowing controlled access to object properties.

- `instanceof` Operator: The `instanceof` operator is used to determine if an object is an instance of a particular class or constructor function.

By mastering these OOP concepts, JavaScript developers can create well-structured, modular, and maintainable code that efficiently models real-world entities and interactions.

Thanks for reading!

Keep Coding!

For more content [click here](https://shefali.dev/blog)!
<a href="https://www.buymeacoffee.com/devshefali" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>