OOP (Object-Oriented Programming) in JavaScript
Tags: javascript, oop, webdev
Published: 2024-04-01 14:06:03

## Introduction to Object-Oriented Programming in JavaScript

Object-Oriented Programming (OOP) is a popular programming paradigm that helps organize and structure code by treating objects as the fundamental building blocks of a program. It provides a way to model real-world entities and their relationships, making code more modular, reusable, and easier to maintain. In this blog post, we will explore the basics of OOP in JavaScript, including prototype definition, prototypal inheritance, and the prototype chain.

In a normal object literal you would define keys and values:

```
const person = {
  id: "1",
  name: "John",
  age: 30,
  
  sayHello() {
    console.log(`Hello ${this.name}`);
  }
}
```

without OOP you will repeat the same code over and over each time you want to define another person.

in OOP we can define the person as a model just once and then instantiate real objects from this model.
Ex: (**This is not a valid code. just for demonstration**)
```
// Person model
const Person = {
  id
  name
  age
  
  sayHello() {
    console.log(`Hello ${this.name}`);
  }
}

const john = new Person('1', 'John', 30);
const doe = new Person('2', 'Doe', 45);
```

To create a model in javascript there are actually three different ways.

But we will take here the right one that allows you to understand it easily.

With constructor functions, you can create a model or a blueprint.
A constructor function is a special type of regular functions, it is used with the new keyword to create instances of a model. This is what makes a function to be a **constructor function** that constructs a model.

Great, let's define a constructor function to build our model from the previous example (Person):
```
const Person = function(id, name, age) {
  this.id = id;
  this.name = name;
  this.age = age;
}
```
Since it is a regular function like any other function, we can leverage the function's arguments to assign values to the properties of each Person instance.
Here we have assigned each argument to its corresponding property.

This is great, but how can we create instances of this model?

Here the **new** keyword comes into play:

To create an object, we have to call the function with the 'new' keyword:
`const john = new Person('1', 'John', 30)`

Actually, when we call a function with the 'new' keyword there are 4 things happened:

1. A new object is created.
2. The function is called and 'this' will point to the newly created object.
3. The prototype object of the constructor function will be linked to the object. (later)
4. The function automatically returns the object.

`console.log(john); // Person { id: '1', name: 'John', age: 30 }`
Now, john's object is created and we can access its property like any other object:
`console.log(john.name); // John`

Now, let's talk about the third feature that 'new' keyword gave to us.

Every JavaScript object has a prototype, which serves as a blueprint for that object.
For our example, the prototype of john is Person.

What does it useful for?
Well, we can take advantage of this behavior by defining our methods in the prototype of john which is **Person.prototype** 
and use it whenever an object needs to use it.
So the function in the prototype defined once. this approach is increasing the performance of our web app.

```
const Person = function(id, name, age) {
    this.id = id;
    this.name = name;
    this.age = age;
};

Person.prototype.sayHello = function() {
  return `Hello ${this.name}`;
};

const john = new Person('1', 'John', 30);
console.log(john.sayHello()) // Hello John;
console.log(john.__proto__); // prints the prototype of John: Person { sayHello: [Function] }
```

Now we can access this method by any object of type Person. this approach is called **prototypal inheritance**, in which the objects is inheriting the methods from its prototype.

But never define a function inside the constructor function:

```
const Person = function(id, name, age) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.sayHello = function() {
    return `Hello ${this.name}`;
    }
};

const john = new Person('1', 'John', 30);
console.log(john.sayHello()) // Hello John;
console.log(john.__proto__); // {}
```

With this approach, we are copying the function to each object created. Imagine we've created 1000 objects of type Person, this will define sayHello function for each object, which of course damaging our performance.


**Prototype Chain**
JavaScript uses a prototype chain to find properties and methods of an object. If a property or method is not found on an object, JavaScript looks up the prototype chain until it finds it.

For example, when we call john.sayHello(), JavaScript first checks if john has a sayHello method. If not, it looks for it in the Person prototype.

**_Conclusion_**
Understanding OOP and the prototype chain is essential for writing clean and efficient JavaScript code. By leveraging constructor functions and prototypes, we can create modular and reusable code that is easier to maintain.