Object Oriented Programming In Javascript: A comprehensive guide
Snevy1
Posted on November 13, 2023
What is object-oriented programming?
Object-oriented programming is a programming paradigm or concept in which we wrap our data and functionality into objects
The world is made up of objects, i.e.person, tree, car,school etc.
When we think and write code in terms of objects it is easier to understand, to reuse and to scale our code.
Object Oriented Programming is different from functional programming in that the latter emphasizes on logic that manipulates objects rather than the object itself while the former focuses on objects and adds logic to it.
A common way of object oriented programming is to create objects, add functionality and data to it. Then find a way in which these objects, their subclass objects and sibling objects relate.
For example, an animal is an object, an animal can eat, can move and can sleep.
We create an animal object. We add functionality to this object.Some of the functions are eat, move and sleep.
Notice that all animals can eat, move and sleep but not all can speak.
A person is an animal that can speak. By itself a person is an object and relates to the animal object because is a subclass of animal but is special because it can speak.
In OOP we find a way to relate the animal object and a person object.
That is to say, we relate a parent object(class) to its children(subclass) or a parent to another parent.
That is quite a mouthful introduction. Let's now focus on OOP in javascript
In javascript, data can be numbers, boolean, string, objects, arrays,functions etc
Functions inside of an object are called methods.
Actually, the term method is just a way to know that the function we are talking about is inside an object.
In javascript there are three main ways to create an object
Let us create a player object and add data and some functionality to it
The common way is using object literal. In this method we create an object then add data and functions immediately.
//First way
const player = {
name: "John",
score: 6,
increment: function () {
player.score++;
},
};
// console.log(player)
When we log player:
We can also create an object by first creating an empty object then add data and functions later
In this way, javascript first asks, Is there a variable called name? if yes, it reassigns its value.If no, it creates a new variable with value and adds it to the object.
//Second way
const player = {}; //Empty object
player.name = "John";
player.score = 6;
player.increment = function () {
player.score++;
};
//console.log(player)
When we log player, you will notice it looks similar to way1:
We can also create an empty object by using inbuilt method in javascript(Object.create(null)) then add data and functions.
So what the hell is this Object.create(null)? We shall answer this question later.
Notice that this third way looks suspiciously similar to the second way above except for the "Object.create()" thing.
//Third method
const player = Object.create(null);
player.name = "John";
player.score = 6;
player.increment = function () {
player.score++;
};
//console.log(player)
Let's log player once more:
Why would we use the third way when the first 2 ways can just do fine? We will answer this question after a few more explanations. Stay tuned.
Functions in Javascript
Functions enable us to implement logic i.e manipulate data, create data, store data etc.
Imagine you are creating a game that requres more than 100 players. Creating an object for each of the player manually is tiresome.
This is where we can use a function that when invoked creates an object for us automatically.The only thing we need to do is to give it a name of the player and a score.
Let's us create a function called playerCreator. Read the comments in the code to understand how javascript runs it.Follow the numbers to understand which code is executed first.
// 1. playerCreator in instantiated and assigned a function definition. Javascript leaves it that way because it is not yet invoked!
function playerCreator(name, score){
//3. Javascript pairs the parameters(name and score) with arguments("John" and 6)
// By this time name has a value of John and score has a value of 6. i.e name: "John", score: 6.
// 4. Create a new empty object
const newObject = {}; //Empty object
// 5. Add property key values to the object
newObject.name = name;
//To the left before (=) we are creating a property(key) called name
//To the right we are giving that property a value that is stored in a variable called name.
newObject.score = score;
newObject.increment = function () {
newObject.score++;
};
/* The final player object looks this way :
newObject = {
name = "John",
score = 6
increment = function(){}
} */
// 6. //We return the newObject.This is the object that playerCreator function has made for us!
return newObject;
}
// 2. playerCreator is invoked
// 7. Assign the value returned by playerCreator() after its ivokation to a variable named player.
const player = playerCreator("John", 6)
const player2 = playerCreator("Doe", 8)
The above code is great... we have solved one problem of not creating every new object by ourselves but you notice that every time an object is created the increment function is also created.
If we log the value of player and player2 we notice that they both have the increment function:
There is nothing special about increment function. It does the same thing for all objects. It would be nice if we can have such a function in just one place from where every object can access it. This saves memory and improves perfomance.
It turns out that we can actually implement this by using Object.Create() introduced earlier.
According to MDN: The Object.create() static method creates a new object, using an existing object as the prototype of the newly created object.
Did you understand what that statement mean? If not don't feel bad, soon it will make sense.
... Creates a new object - This seems like what we initially did by creating an empty object in the playerCreator function right? But this time this method does it for us
... Using - For a function or a method to use something you must give it that thing right? We give Object.create something by passing it in the bracket i.e Object.create(somethingthat we give).
... an existing object - Where is this existing object? It must be somewhere right? It turns out that maybe there isn't and therefore the method will use null as a parameter or If there is an existing object it will use it i.e Object.create(an existing object).
...as the prototype - What is a prototype? Every object in javascript has a its own property called a prototype. What does this even mean?
First, remember that an object has property: key value pairs. These properties and keys can be created or are inbuilt.
These properties can be a name with a value of a string, an object with a value of an array or an object with a value of an object etc
Prototype - This is an inbuilt property of an object in javascript which by itself is an object and has its value as an object
Consider the code below:
object1 = {
name: "John",
prototype: {
//I am an object, a value of prototype
someFunc: function () {
//do something...
},
},
};
Since a prototype is an object, and is a property of object1, object1 can access prototype's data i.e Object1.prototype.someFunc
..of the newly created object
In short, in our case Object.create(someObject or null), first creates a new object (Object1) then uses Object1's property(prototype property) to assign data of someObject to prototype. The code snippet above becomes:
object1 = {
name: "John",
prototype: {
//someObject's data
someFunc: function(){
}
}
}
To summarize, we can use Object.create() to pass in an object that has all functions and data that are common to all players and so each player
will have access to this object's data because there is a bond created between the prototype(the passed in object) and the newly created object.
Wow! That is alot. Consider taking a break before continuing.
In this section, let's start by creating an object that stores all of the common functions of players
const playerFunctionsStore = {
increment: function () {
this.score + 1;
//Whenever you use "this" keyword inside of a method, 'this" refers to the object on which the method was called on
// In this case, the object on which increment was called on is player, so javascript replaces "this" with player
// hence this.score++ will be player.score++
},
login: function () {
console.log("logged in");
},
};
let's create a function that will create objects for us.
The new objects will have playerFunctionsStore's object data assigned to their prototype property(This is done automatically by Object.create())
The bond between the newObject and its prototype is fomarlly called _ _ proto_ _.
To understand more read the comments in the code
function playerCreator(name, score){
//Object.create creates a new empty object using an existing object(playerFunctionsStore) as prototype of the newly created object
let newObject = Object.create(playerFunctionsStore)
//In addition to Object.create giving us a new object, it also creates a bond(_ _proto_ _) between the newly created object, newObjectand its prototype that we passed into the brackets.
//By default, an object inherits functions(method) and data from its prototype thus because playerFunctionsStore is a prototype of newObject, newObject can access functions such as login and increment that are stored in playerFunctionsStore
//To access properties of its prototype, newObject uses the bond/link officially called _ _proto_ _
// _ _proto_ _ is a property of objects including newObject that javascript uses to look for data in the inheritance chain
newObject.name = name
newObject.score = score
return newObject
}
let player = playerCreator("John", 9)
let player2 = playerCreator("Doe", 8)
// The score has increased by one
You notice from the image below that increment and login function are not on the player and player2 objects.
To see these functions we expand on the _ proto _:
This is a great solution in that it saves memory however, in modern javascript we don't want to leave our information in a global store.
What if someone accidentally deletes the store? Then all of our information will be lost.
It would be nice if there is no global store for functions but rather the store is an object inside of a function. We will address this issue in the next section.
Also, what if there is a way in in which we don't need to use or write the Object.create() to create objects but this can be done for us automatically?
It turns out there is: The new keyword :)
In the next section of this article we will dive into the use of the new keyword
The new keyword
let's start by recreating the objects and functions we have used previously.
The new keyword before a function invokation does four things:
- Automatically create a new object
- Assign this keyword to the newObject hence this.name in the playerCreator() will be newObject.name during execution
- Javascript automatically creates _ _ proto _ _ and assigns it to the prototype property of playerCreator (the function that is to the right of the new keyword)
- Automatically returns the newObject out of that function
Please follow the numbers to understand the sequence in which javascript is executing the code. Also read the comments.
// 1. Javascript instantiates a variable called playerCreator and assigns a function definition as its value
function playerCreator(name, score) {
// 7. Javascript by using the new keyword assigns "this" a value of newly created object
this.name = name;
this.score = score;
// 8. Returns a newObject automatically. No manuall writing of return
}
// 2. Javascript will look for playerCreator then look for its key called prototype
// 3. Then look if the prototype key has increment function if not it will create the function and store it in the prototype key(property).
playerCreator.prototype.increment = function () {
this.score++;
};
// 4. Javascript will do the same as in 2 and 3
playerCreator.prototype.login = function () {
this.login = false;
};
// 5. Javascript will invoke the playerCreator function
const player = new playerCreator("Jose", 8);
console.log(player);
We realize that we get the same result as we would use Object.create()
The downside of this method is that we have to Capitalize the first letter of the creatorfunction so we know it requires the new keyword
Because of this the class syntax was introduced.
The class - Syntactic sugar
Introduced in 2015
It is cleaner but no perfomance benefit
Majorly used in modern frameworks such as React.
The main diffence in syntax is:
- All the shared methods are put together in the constructor so there is no playerCreator.prototype.someFunction
- the constructor replaces the this.name, this.score and removes the parameters from being passed directly in the playerCreator function
Look at the images below to understand the difference. The replaced parts have corresponding highlighted colors
This has been a long article! I think it is worth it though.
In the next article that I will be rolling out soon, we will discuss more on classes, and introduce the Factory functions
Wish you well in your programming journey!
Connect with me on:
Open for technical writing roles.
Posted on November 13, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 27, 2024