I Do Not Know Object Oriented Programming!

resourcefulmind

Opeyemi Stephen

Posted on December 30, 2021

I Do Not Know Object Oriented Programming!

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"
};
Enter fullscreen mode Exit fullscreen mode

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."
Enter fullscreen mode Exit fullscreen mode

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.";}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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", 
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
   }
}
Enter fullscreen mode Exit fullscreen mode

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!!")
   }
}
Enter fullscreen mode Exit fullscreen mode

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);
   }
};
Enter fullscreen mode Exit fullscreen mode

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

  • 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");
Enter fullscreen mode Exit fullscreen mode

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");
  }
};
Enter fullscreen mode Exit fullscreen mode

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");
   }
};
Enter fullscreen mode Exit fullscreen mode

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
};
Enter fullscreen mode Exit fullscreen mode

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

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.

💖 💪 🙅 🚩
resourcefulmind
Opeyemi Stephen

Posted on December 30, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related