I Do Not Know Object Oriented Programming!
Tags: beginners, javascript, webdev, programming
Published: 2021-12-30 19:29:08

If you are a beginner who is currently getting their butts whooped by JavaScript or swimming in tutorial hell, I'm quite sure you must have read the title of this article and given me an imaginary hug because you can also resonate. Truth is, I do not have a ready made blueprint to help you understand OOPs, but as I once read somewhere that the best way to learn is to teach...so here I am, Fun and Janae Monelle's "We Are Young" playing in the background, about to share my own personal notes about Object Oriented Programming. I hope this helps a newbie somewhere. 

**PS: I would welcome contributions, resources and comments that would help other newbies. We can burn brighter than the sun if we all help each other.**

So the first thing we all need to know is the conventional definition...

**_Object-oriented programming combines a group of data attributes with functions or methods into a unit called an "object. Multiple independent objects may also be instantiated—or represented—from the same class and interact with each other in complex ways._**" 

Typically, OOP is class-based, which means that a class defines the data attributes and functions as a blueprint for creating objects, which are instances of the class. 

I happen to love cars alot so my first simple example would be to consider a class representing a car. The "car" class will contain  attributes to represent information such as the car's name, model, number of wheels, color, etc. Maybe this would be more familiar;

```
let car = {
     name: "Mercedes Benz",
     model: "CLA 4DR Coupe", 
     numOfWheels: 4, 
     chassisNum: 0123456789, 
     color: "white"
};
```

_I'd like to go on and talk about what everyone refers to as the 4 basics of Object Oriented Programming which would be Encapsulation, Abstraction, Inheritance and Polymorphism...but before I start throwing these words around, wouldn't it be better if we really understood how to use OOPs and then saw these 4 basics in action for ourselves?_

---
We already successfully created our first class with their different properties and values. We can access the properties and subsequently, the values in our "car" object using the **Dot Notation**. Take a look at the code below;

`console.log(car.model); //"CLA 4DR Coupe"`

In the code above, we used the **dot notation** on the object named **"car"** and then followed by the property **"model"** to access the value which is **"CLA 4DR Coupe"**

_Cool right?_  We might have private data in the class, such as "chassisNum" that should not be exposed to other objects in the program. By encapsulating this data member as a private variable in the class, outside code would not have direct access to it, and it would remain safe within that person’s object. 

In OOP, we encapsulate by binding the data and functions which operate on that data into a single unit, the class. 

By doing so, we can hide private details of a class from the outside world and only expose functionality that is important for interfacing with it. When a class does not allow calling code access to its private data directly, we say that it is well encapsulated. **There there, you just understood Encapsulation.**

---
It will be pointless to learn about OOPs without knowing what **METHODS** are. **Methods** are a special type of property that objects have. They are simply properties that are functions. They add a different behavior to an object. I like to think that they make objects a little more flexible in doing stuff. For instance, 
```
let car = {
  name: "Range Rover Evogue", 
  price: 70000, 
  describeCar: function() {
    return "That car speeding on the highway is a " + car.name + " and it costs " + car.price + " USD.";}
};

car.describeCar(); //"That car speeding on the highway is a Range Rover Evogue and it costs 70000 USD."
```
The block of code above has a method `describeCar` which is a function and returns a statement telling us the name and Price of the car.(BTW, I have no idea about the price of the Range Rover).
Notice that the method accessed the `name` and `price` property in the return statement using the `car.name` and `car.price`. Now think about the many awesome things you can do with Methods...sweet right? 

---

There is another way to access the `name` and `price` properties though...yeah, you probably heard about it...the **"this"** keyword (At this poin, you're probably like...who was behind naming these coding concepts, because what is literally "this" right? lol)

**"this"** in my own opinion, which I would like to think is shared by others, exists to make code reusable and very much easier to read.

In the last example, we had a method `describeCar` which used `car.name` and `car.price` dot notation to access the values for the `name` and `price` property within the return statement.
Recall, 
```
 describeCar: function() {
    return "That car speeding on the highway is a " + car.name + " and it costs " + car.price + " USD.";}
```
Although, it is a very correct way of accessing the object _"car"_ 's property, have you ever asked yourself what happens when you have accessed this object and its properties on lines 235, 410, 720, 850, 1100, 1425, 1658, 1780 and 3800 of your codebase and for some reason, the variable name changes from `"car"` to `"automobile"` while working for a large company such as Mercedes?

Your work gets extra stressful as you have to update all those lines of code which references the original name that was changed and we both know how stressful that can get. This is where the **`this`** keyword comes in. You can have your initial code in our last example re-written like this;
```
let car = {
  name: "Range Rover Evogue", 
  price: 70000, 
  describeCar: function() {
    return "That car speeding on the highway is a " + this.name + " and it costs " + this.price + " USD.";}
};

car.describeCar();
```
Now, we have barely scratched the surface and `this` is a very deep and sometimes complicated subject and the above is definitely not the only way it can be used. Here, we just used `this` in referring to the object that the method `describeCar` is associated with, which is `car`. By virtue of this, if the object variable `car` is changed to `automobile` or even  `locomotive`, it is not necessary to find all the references to `car` in the code. There you go...easier and reusable across board. 

Now that we have got that out of the way, let's be civil engineers for a bit and talk about Constructor Functions(this is me trying to make a joke that's not funny btw)...

---
Now, imagine that you are seeing the function below for the first time as a beginner...which is probably what's happening right now;

```
function Truck() {
  this.name = "Ford Ranger 2018";
  this.color = "Black";
  this.price = 100000;
  this.numWheels = 4;
  this.yearOfProduction = 2018;
}
```
Looks weird right? Because it did look weird to me when I looked at it for the first time ever too. Functions are supposed to return a statement or value or whatever else you read up yeah? It also looks like an object or even a method but methods ar always inside the object and this isn't how "normal" objects are written...Don't fret, **this is a Constructor Function**

**_Constructors are functions that create new objects._** They define properties and behaviors that will belong to the new object. What this means is that like the example above, functions written that way will create a new object called **"Truck"** and append the `name`, `color`, `numOfWheels` and `yearOfProduction` properties and their corresponding values to the object. The `this` refers to the new object that has been created.

Take note that the `Truck` object was defined with a _capital letter_. Constructors are defined this way to differentiate them from other functions that are not constructors and will not return values like other functions would. 

And as usual, a new problem will always arise from an existing one...what if we want to create a new object which will have the same properties as our initial `"Truck"` constructor from the our previous example? We simply add the following line of code beneath the previous code block like so;

`let fordTruck = new Truck();`

The **`new`** operator will instruct JavaScript to create a new copy of the **`Truck`** object called **`fordTruck`**. 

**Take note that if you do now include **`new`**, you will not get the result as no new object will be created even if you troubleshoot and console.log from here to Bethlehem** 

So ultimately, if you type `fordTruck.name` in your console, the result will give the value of our initial Truck's `this.name` because `fordTruck` now has all the properties of `Truck`.

---
Now you know what constructors do, but if you are the obsrvant genius I know you are, then you would notice that when we created the new constructor `fordTruck`, it took the `name` property along with the other properties such as `color`, `numOfWheels` and yearOfProduction. We can keep changing the names as we go by if you want different values for each new `Truck` but supposing you are in charge of keeping track of hundreds of thousands of trucks produced at the Ford Plant? 

You can change or easily create new instances of the Trucks by designing the initial `Truck` constructor to accept whatever parameters might need to be changed like the name of the truck, the price, the color and still leave the other values to remain the same if you want. **So we re-write the original constructors to accept arguments as shown below;**

```
function Truck(name, price, color) {
   this.name = name;
   this.color = color;
   this.price = price;
   this.numWheels = 4;
   this.yearOfProduction = 2018;
}
```
And then we can say;

`let fourWheel = new Truck("Ranger", 175000, "gray");`

When you do this, you create a new instance of `Truck` which will be named `fourWheel` and will set the properties to the new properties of the new `fourWheel` object.

With the above, the constructor function is now very flexible as it can accept parameters and we can define new properties for each truck when they are created. 

**Always keep in mind that constructor functions group objects together based on shared characteristics and behavior and define a blueprint that automates their creation**

If you want to check if the new object you created is an instance of the constructor, use the `instanceof` operator.

For instance, in our last example above, 

`fourWheel instanceof Truck;`

It will return `true` because the `fourWheel` object was created using the `Truck` constructor.

But if we say,

```
let saloonCar = {
   name: "Ford Focus", 
   color: "white", 
}
```
And then we check the same `saloonCar instanceof Truck;`, it will return `false` because `saloonCar` was not created using the `Truck` constructor.

Also, the `Truck` constructor defines five properties (name, color, price, numOfWheels, yearOfProduction) which are defined directly inside it. These properties are called _"Own Properties"_.

Let's assume we are setting up 3 new instances of `Truck` called `firstCar`, `secondCar` and `thirdCar` respectively, we would have something like this;

```
let firstCar = new Truck("edge", "red", 30000);
let secondCar = new Truck("broncos", "black", 120000);
let thirdCar = new Truck("focus", "blue", 65000);
```
The other two properties `numOfWheels` and `yearOfProduction` will remain unchanged as no new parameters were passed in for those. 

All 5 properties are referred to as **Own Properties** because they are defined directly on the instance object `Truck`. This means that `firstCar`, `secondCar` and `thirdCar` all have their own separate copy of these properties and every other instance of `Truck` will also have their own copy of these properties.

What is the essence of all of this and what might we do with the Own Property you might ask...well we could push them to an empty array while writing our code like so;

```
let ownProps = [];
for(let property in secondCar) {
   if(secondCar.hasOwnProperty(property)) {
       ownProps.push(property);
   }
}
```
So that when we `console.log(ownProps)`, it will print the different properties from `secondCar` into the empty `ownProps` array.

---


If you take a close look at our code, you should definitely see that `numOfWheels` has the same value for all instances of `Truck`. In other words, it is sort of a duplicated variable. 

It is not much of a problem if you have only a couple of instances or say 5 instances of the original car object...but...you will likely be working at the Ford HQ and using your code to keep track of millions of 4-wheeelers which means millions of instances. 

In situations like the above-listed, a **`prototype`** comes in very handy. What does the prototype do you might ask? Simple..A **prototype** shares a particular property amongst all instances of the original object.

`Truck.prototype.numOfWheels = 4;`

Now all instances of `Truck` will have the `numOfWheels` property.

The `prototype` for `firstCar` and `secondCar` is part of the `Truck` constructor as `Truck.prototype`.

In summary, when it comes to properties, own properties will always be defined directly on the object itself while prototype properties will be defined on the prototype.

So what if we have to add more than one property to our prototype? You already know that would be very cumbersome of we had to do that one after another. A more efficient way would be to set the prototype to a new object that already contains the properties. We have this below;

```
Truck.prototype = {
   numOfWheels: 4, 
   sound: function() {
     console.log("Vroom! Vroom!!")
   }
}
```
And then we want to add a `quality` method to the prototype. All the properties can be added at once in this manner like so;

```
Truck.prototype = {
   numOfWheels: 4, 
   sound: function() {
     console.log("Vroom! Vroom!!")
   },  
   sound: quality() {
     console.log("It is a super fast " + this.name);
   }
};
```

**NEVER FORGET** to always define the constructor property whenever a prototype is manually set to a new object. Why? Well the reason is quite simple, it is beacuse when you set the prototype manually, it will erase the constructor property and if you check which constructor function created the instance, the results will be false.

Summarily, for a better understanding of the prototype chain, you need to always take note of the following;

- All objects in JavaScript have a prototype(save for a few exceptions).

- The prototype of an object is an object. If this confuses you, you can bet it confused me too. You should check out [Javascript.info](https://javascript.info/search/?query=prototype&type=article)

- A prototype can also have its own prototype because a prototype is an object. For instance;

```
function Car(name) {
  this.name = name;
}

typeof Car.prototype; //the result for this will be "object"

let bugatti = new Car("Veyron");
    bugatti.hasOwnProperty("name");
```
From the above, 
`Car` = supertype for `bugatti`
`bugatti` = subtype for `Car`
`Car` = supertype for `bugatti`
Object is a supertype for both `Car` and `bugatti`
Object is a supertype for all objects in JavaScript, therefore, any object can use the `hasOwnProperty` method.

---

There's another important principle to be observed before I take a pause on this is the principle of **Inheritance**.

Repeated code is usually a problem because any change in one place requires fixing the code in multiple placesbwhich would just give devs more work and make them more likely to make errors. Now let's say we have two constructor functions which I will name after two of the biggest artistes in Africa(just because I can and we don't have to always be boring);

```
Wizkid.prototype = {
   constructor: Wizkid, 
   describe: function() {
      console.log("My name is " + this.name +  " and I always come late to my concerts in Nigeria");
  }
};

Davido.prototype = {
   constructor: Davido, 
   describe: function() {
      console.log("My name is " + this.name + " and I always come late to my concerts in Nigeria");
  }
};
```
As we can see, the `describe` method is repeated in two places and we can use what we call the **DRY principle** (Don't Repeat Yourself) to refine this code by creating a supertype called `**Artistes**` like so;

```
function Artiste() {};

Artiste.prototype = {
    constructor: Artiste, 
    describe: function() {
       console.log("My name is " + this.name + " and I always come late to my concerts in Nigeria");
   }
};
```
Since you have the above supertype `Artiste` which includes the `describe` method, you can then remove the `describe` method from `Wizkid` and `Davido`.

```
Wizkid.prototype = {
  constructor: Wizkid
};

Davido.prototype = {
  constructor: Davido
};
```
There you go, you just successfully created a supertype called `Artiste` that defined behaviors shared by all musicians/artistes.

I will stop here for now...you can learn more about the basics of Object Oriented Programming as well as advanced concepts on [Javascript.info](https://javascript.info/search/?query=object+oriented+programming&type=article)

You can also chip in via the comments for other newbies to learn more as I have barely even scratched the surface. Godspeed and Happy New Year in advance to you and yours.