# Understanding OOPS in JavaScript

## What is OOP?

**OOP** (Object-Oriented Programming) is an approach in programming in which properties and methods are grouped/packed within objects.

**Code Example :**

```javascript
class Cart {
  constructor() {
    this.items = [];
  }

  addItem(item) {
    this.items.push(item);
  }

  getTotal() {
    let total = 0;
    for (let item of this.items) {
      total += item.price;
    }
    return total;
  }
}

const cart = new Cart();

cart.addItem({ name: "Laptop", price: 50000 });
cart.addItem({ name: "Mouse", price: 500 });

console.log(cart.getTotal()); // 50500
```

## Functional Programming

Functional Programming is a style of programming where problems are solved by **combining pure functions that take inputs and return outputs without modifying existing data**.

**Code Example :**

```javascript
const items = [
  { name: "Laptop", price: 50000 },
  { name: "Mouse", price: 500 }
];

function getTotal(items) {
  return items.reduce((total, item) => total + item.price, 0);
}

console.log(getTotal(items)); // 50500
```

## Why OOP when Functional was working fine?

*   When our codebase become large, OOP approach makes it easy to manage code and it also **reduces messy and spegiti code.**
    
*   Sometimes we need to combine the primitive datatypes together to create a complex datatype. Example : I want to store user details, so I want his details to be combined together so that it becomes easy to work with it.
    
*   Imagine if Javascript has all the functions individually present rather than encapsulated in their objects, then how difficult it will be to work with it.
    

![](https://cdn.hashnode.com/uploads/covers/6969157de175d100faf5be39/15960448-1b0c-4c39-931c-b5f74f48a36b.png align="center")

## 4 Pillars of OOP

There is trick to remember these 4 words:

> EAIP - Every app is powerful.

*   **Encapsulation :** Combining data togther like methods and properties in object.
    

Example: A capsule combines all the chemicals together.

*   **Abstraction :** Hiding the core details and only showing necessary ones.
    

Example : Complex functionality inside our mobile phones which we do not see.

*   **Inheritance :** Extending functionality from existing functionality.
    

Example : Child inherits some traits from his parents.

*   **Polymorphism :** One thing but acts differently with different inputs.
    

Example : Method overriding

## Why do we need to create multiple objects

Here is a real life example of why we need to create multiple objects inside the file.

Suppose I am writing a code where I want to throw custom error at multiple places. So I will simple inherit the Error class and throw after creating its object rather than creating object every time from zero.

```javascript
// case 1
throw {
    name : 'Type error',
    message : 'error occured'
}

// case 2
throw {
    name : 'Logical error',
    message : 'error occured'
}

//case 3
throw {
    name : 'Db error',
    message : 'Db connection failed'
}
```

```javascript
class myError extends Error{
    constructor(name, message){
        this.name = name;
        this.message = message;
    }
}

// case 1
throw new myError('Type error', 'Error occured')

// case 2
throw new myError('Logical error', 'Error occured')

//case 3
throw new myError('Db error', 'Db connection failed')
```

## Creating Objects using template literal

Try not to confuse Objects too much, just remember, Object is just a combination of methods and properties together.

Whatever way we choose to create an object, our only **goal is to make an object** and **object is a collection of properties and methods.**

The simplest and easiest way to create an object is through template literal :

```javascript
let user = {
    name: 'Rohan Singh',
    age: 21,
    intro : function () {
        console.log(`My name is ${user.name}`);
    }
}
```

In the function above I used [user.name](http://user.name) to access name key value inside user object.

## Introduction of this keyword

> If I can access object using variable name (user), why do we need 'this' keyword?

Let us understand with an example :

```javascript
let user = {
    name: 'Rohan Singh',
    age: 21,
    intro : function () {
        console.log(`My name is ${user.name}`);
    }
}

let user2 = user;
user = null;
user2.name = 'Kartik Kumar';

console.log(user2.intro());
```

The above code will give an error message :

![](https://cdn.hashnode.com/uploads/covers/6969157de175d100faf5be39/ca068247-f026-4373-83e9-430a304d7f38.png align="center")

The error came because I was accessing name property of the object using **user** variable name which is very specific and user is now null. We want some generic word instead of specific **user** , so that our object works when another variable referencing to it.

The solution is **this** keyword.

Just remember these two things related to 'this' keyword and you will understand 'this'.

![](https://cdn.hashnode.com/uploads/covers/6969157de175d100faf5be39/16728955-c76e-4800-9c19-e480677f3603.png align="center")

'this' points to the current context whoever calls the function.

It means if user.intro() is called then 'this' points to user object

If user2.intro() is called then 'this' points to the user2 object.

Code example using this keyword:

```javascript
let user = {
    name: 'Rohan Singh',
    age: 21,
    intro : function () {
        console.log(`My name is ${this.name}`);
    }
}

let user2 = user;
user = null;
user2.name = 'Kartik Kumar';

console.log(user2.intro()); //here this = user
```

## Introduction to new keyword

### Creating multiple objects through Factory Functions

Factory Functions allow us to create multiple objects. Below is the code example :

```javascript
function Car(name, model) {
    return {
        name,
        model,
        intro() {
            console.log(`${this.name} is ${this.model} model.`);
        }
    }
}
const bmw = Car('BMW', 2015);
const maruti = Car('Maruti', 2026);
```

> In the above code, I am able to make multiple objects, Car function is acting like a class template here, then why do we need classes and new keyword?

### **Creating Multiple objects using Constructor Functions**

To understand new keyword, let us first create objects using constructor function.

```javascript
function Airline(name){
    this.name = name;
}

//functions are also treated as object that's why we are adding intro function to its prototype
Airline.prototype.intro = function () {
    console.log(this.name + 'is great')
}

const indigo = new Airline('Indigo');
const emirates = new Airline('Emirates');
```

Now let us understand what new keyword is doing here. It does 4 major things when used with constructor functions :

```javascript
1) Create an Empty Object ====> const obj = {};
2) Function and Object prototype is linked ====> obj.__proto__ = Car.prototype;
3) bind Car with obj ====> Car.bind(obj, 'BMW', 2015);
4) Returns that object automatically ====> return obj;
```

### Factory Function vs Constructor Function

*   **Memory Efficiency** : Each object has its own copy of all methods in case of factory function whereas methods live in one shared place and all objects share the same function referrence.
    
    ```javascript
    //PROOF
    
    console.log('Are methods shared in Factory function objects : ' + bmw.intro === maruti.intro)); //false
    
    console.log('Are methods shared in Constructor function objects : ' + (indigo.intro === emirates.intro)); //true
    ```
    
*   **instanceof keyword** : It only works if object is not created through factory function.
    

```javascript
console.log('Does instanceof works in Factory Function objects : ' + (bmw instanceof Car))

console.log('Does instanceof works in Contructor Funtion objects : ' + (indigo instanceof Airline))
```

## ES6 Classes

Classes are just a syntactical sugar over constructor functions.

```javascript
function Company(name, employees){
    this.name = name;
    this.employees = employees;
}
Company.prototype.intro = function () {
    console.log(this.name);
}

// converting constructor function to classes syntax

class Company{
    constructor(name, employees){
        this.name = name;
        this.employees = employees;
    }

    intro(){
        console.log(this.name);
    }
}

const yiron = new Company('Yiron', 80); 
const infosys = new Company('Infosys', 150); 
```

## Intro to prototypes

For now prototype is just another property, inside the object which is there by default.

## Regular function this vs Arrow function this

| 🔎 Feature | 🧩 Regular Function | ⚡ Arrow Function |
| --- | --- | --- |
| `this` **Keyword** | Has its own `this` | ❌ Does not have its own `this` (lexically inherited) |
| `arguments` **Object** | Available | ❌ Not available |
| **Hoisting** | ✅ Function declarations are hoisted | ❌ Not hoisted (stored in variable) |
| **this** | this is binded where function is called not where it is defined | this is binded where the arrow function is defined, no matter where it is called |
| **Binding** | this can be explicitly binded using call, apply and bind | this cannot be explicitly binded in arrow functions |
