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
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
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.
Top comments (2)
My sincerest apologies for this. I wanted to click something else. I have done the needful now. Your comments are highly appreciated
Some comments may only be visible to logged-in visitors. Sign in to view all comments.