Object-Oriented Programming Concepts in JavaScript - The Hard Way
Before we start discussing the hard way of object-oriented programming concepts in JavaScript, I wanna highlight that this concept is the most difficult version.
So if You want to read easier reference, I suggest you look for others. 😅
But if You curious and force yourself to read the discussion below, my hat is off to you, why?
Because in my opinion, if we understand the most difficult version, we can easily understand the easier version as well. Its piece of cake, isn't it?
Let's get started, the first thing we have to know before understanding the difficult concept of object-oriented programming in JavaScript is the object, this, class and prototype. They are all will be discussed in point 1 - 4.
Table of Contents
Object
Anyway, while in point 5 - 8, we will discuss the object-oriented programming in JavaScript itself. If You feel confident about the basic, You can jump up on to the core discussion directly.
Object is one kind of data type in JavaScript which consists of several keys and values. The values can be any others data type in JavaScript, like number, string, boolean, array, function or event another object.
var object = { number: 7, string: 'this is string', boolean: true, array: [1, 2, 3], func: function() { return 'ini fungsi' }, otherObject: { number: 3, string: 'this is other string', boolean: false, array: [9, 8, 7] }}
On the highlighted line, number
is a key and 7
is a value.
To check whether an object has a certain property or not we can use hasOwnProperty
method. This method takes one string parameter which is the key we wanna check.
object.hasOwnProperty('number')// true
object.hasOwnProperty('notRight')// false
On the snippet above, hasOwnProperty
method return a boolean.
Global Object
By default, JavaScript run in the browser will have a global object. It is window
. If we write window
on the browser console as follows:
window
that code will displayed,
The global object contains following things:
- All JavaScript public API, like
parseInt
andparseFloat
- All JavaScript's data type object, like
Array
andJSON
- The variable we declared using
var
keyword - The function we declared
There are 3 ways to declare a variable in JavaScript, using var
, let
or const
keyword. If we declare a variable using var
keyword, it will be attached to the global object automatically.
var name = 'jefrydco'
window.name// 'jefrydco'
name// 'jefrydco'
let say we declare a variable called name
using var
keyword. We can access that variable using window.name
or only name
. That won't applicable if we declare it using let
and const
keyword. Moreover about let
and const
will discuss in another chance.
Declaring a function will be attached to the global object as well,
function func() { return 'this is function'}
window.func()// 'this is function'
func()// 'this is function'
so we can call it using this way window.func()
or func()
directly. Like the variable, it won't applicable if we declare the function using let
and const
keyword.
So we can think that global object window
is a predefined variable that contains all of the things we need to use JavaScript.
this
In my opinion, this is one of the hardest concepts to understand in JavaScript besides of object-oriented programming.
By definition, this
is a context where the code is executed. this
keyword will often used in object-oriented programming. There are several contexts of this
we need to understand.
this in Global Context
The first one is this
in global context. By default, this
in global context will refer to global object itself which is window
. If we type the following code on the browser console:
this
it will result,
We can also check whether this
in the global object is really referring to the global object window
or not using this way:
this === window// true
it will return true
.
this in Object Context
The second one is this
in the object context. this
in this context will refer the object before the dot ".".
Take a look on the following code:
var student = { name: "Budi", age: 12, greet: function() { console.log(this.name) }}
student.greet()// Budi
Actually, we can do the same way without using this
by referring it directly to the container variable like as follows:
var student = { name: "Budi", age: 12, greet: function() { // change "this" with "student" console.log(student.name) }}
student.greet()
Because of that, if we use this
, the bug will be prevented.
Not like any other programming language that the value of this
determined in compile-time, in JavaScript, it determines in the runtime and depends on the object before the dot symbol ".".
var teacher = { name: "Deni"}var student = { name: "Budi"}
function greet() { console.log(this.name)}
teacher.func = greetstudent.func = greet
teacher.func()// Denistudent.func()// Budi
In the snippet above, we declare two kinds of objects called teacher
and student
which has their own name
property.
We also declare a function called greet
. Inside the body of the function, we wanna know the value of name
property, even though we never declare it. Then it attached to each object we declared previously with the name func
.
After that we executed the func
function by calling teacher.func()
and student.func()
. They give us a different name
property match with each of two objects has.
But take care on certain cases, the value of this
can be gone, like as follows:
var time = 13
var student = { goodMorning: function() { console.log(this.name) }, goodAfternoon: function() { console.log(this.name) }}
(time >= 12 ? student.goodAfternoon : student.goodMorning)()
It will returns,
To get better understanding why this happen, we have to know how a function is executed.
If we call student.goodAfternoon()
, behind the scenes JavaScript use the dot symbol to return internal data type called Reference Type.
It consists of:
- base (this is the object before dot symbol)
- name (this is the name of the function after the dot symbol)
- strict (it is boolean whether current execution time using strict mode or not, "use strict")
And the result of student.goodAfternoon
isn't a function, but a ReferenceType
which consists of value like as follows:
(student, 'goodAfternoon', false)
When the bracket ()
is called on ReferenceType
, the value of this
is called and arranged accordingly with the value of base
in ReferenceType
. For that case it is the student
.
Assignment operator =
can also remove the ReferenceType
. Because of that the value of this
is gone in the example above.
Take a look on the easier snippet below:
var student = { name: "Budi", greet: function() { console.log(this.name) }}
// Assignment operator to make ReferenceType gonevar greet = student.greet
greet()
Fungsi greet
memiliki ReferenceType
sebagai berikut:
(student, 'greet', false)
In the body of greet
function, we wanna know the value of name
property. Then we attach it into new variable which has the same name as the function. After that we call the variable as a function.
It will returns,
Even though in the body of greet
in student
object we use console log to find out the value of name
property when it attached to another variable, that function never knows the value of name
. It is because the ReferenceType
becomes:
(window, 'greet', false)
Global object window
never has name
property, so that when the greet
function is executed it won't print anything.
this in Function Context
The last one is this
in function context. As in the global object that a function we declared will be attached automatically in global object window
if we aren't declared it using let
and const
keyword.
And in this in object context also discussed if there is a function inside an object, this
inside the function is referring to the object itself.
Because of that, if we use this
inside a function, it will refer to the object itself and the object is global object which is window
.
function func() { return this}
func()
will return,
We can also check whether this
inside a function really refer to global object window
or not using this way:
func() === window// true
We can use strict equality operator, ===
to check whether a variable referring to a similar value or not. We already use this way in the previous section.
By the way, this blog uses ligature font in the block code section, so if I write some operator which consists of 2 characters or more will be displayed differently.
For instance, strict equality operator ===
, greater than or equal operator >=
and smaller than or equal operator <=
. The font is Fira Code.
Class
Class is blueprint of the object. You can take a look at the image below. The left section is the technical specification of aircraft and the right one is the aircraft implementation on real life.
We can imagine the class is like the technical specification. It will tell each of the aircraft components as a detail. From that technical specification, we have a clear rule on how will the aircraft is shaped later.
So are the class. The class describes the object in detail and contextual.
Let say we want to make an aircraft application to record departure schedule. From that case, we need a class to save aircraft data, but we don't need to describe all the detail of the aircraft itself. We might only need the aircraft code, name, and the year.
Another case if we want to make an application to design the aircraft. We need to describe it as detail as possible. Even we need to describe the smallest component available.
That's what I mean by context, we need to describe the object as needed.
If some of you already learn object-oriented programming in another programming language, you might be familiar with the following code:
class Cat { full = false
eat() { console.log('Cat eats...') this.full = true }}
That syntax is available in EcmaScript version 6 or better known as ES6. ES6 is the next version of JavaScript. Unfortunately, in this discussion we won't learn object-oriented programming using that way.
We learn the EcmaScript version 5 way, which is the regular JavaScript we already know today.
We will make a class in JavaScript using function. Take a look on the following code:
function Cat() {}
There are several things we need to know in order to make a class using a function in JavaScript,
The first one, a class is made using function
keyword followed by the class name. From the example above, the class name is Cat
.
The second one, there is an unwritten rule in naming class in JavaScript. It suggests us to use camel case to write the class name.
Camel case is writing rule which each of the words started with capital. For instance, Cat
, PersianCat
and FlyingAircraft
.
Like we already discuss above that class is the blueprint of the object. From that class we can create the real object as follows:
var cat = new Cat()
The real object is made using new
keyword followed by corresponding class, then opening and closing bracket, like when we called a function.
Class Property
To describe class property, we can use this.
(this followed by dot symbol) keyword followed by property name.
function Cat() { this.full = false}
The example above, cat has full property.
var cat = new Cat()
cat.full// false
We can know the value of the property by calling it using dot notation. From the example above, we use cat.full
to know the value.
Class Method
We can be able to create a method inside the class. A method is a function which can be used to change the property value. We can declare a method using the same way as declaring a variable. The only difference is the value is a function. The example is as follows:
function Cat() { this.full = false
this.eat = function() { console.log('Cat eats...') this.full = true }}
From the example above, cat class has eat method. Inside that method, the value of full
is changed from false
to true
.
var cat = new Cat()
cat.full// false
cat.eat()// Cat eats...
cat.full// true
We can also call the method using the same way as calling variable which is using dot notation. The only difference is it must be followed by opening and closing bracket as follows, cat.eat()
.
Defining method using that way as a con. Each time we create a real object from the class, the JavaScript will create a new instance of the method as well in memory.
Imagine if we create several object from Cat
class above. Let say, cat A, cat B and cat C. Eventhough they are the same instance of cat, has same full
property and eat
method, they are surely has different personality.
Cat A perhaps is a greedy cat, almost an hour it eats. Cat B perhaps not too much eating, but it sleeps a lot. Cat C rarely eats but it is the fattiest one.
So it makes sense that the only thing we need to differentiate is the full
property they have. Each of them has their own full time.
We don't need to differentiate how they are eating or for the context of that case, the eat
method. They can use the same method.
We can check whether that method is different one and another using the following code:
var catA = new Cat()var catB = new Cat()var catC = new Cat()
catA.eat === catB.eat// false
catA.eat === catC.eat// false
catB.eat === catC.eat// false
They are all different method because when they are compared, the comparison will return false
.
Because of that JavaScript give a special way to make a method using the prototype. It guarantees that no matter how much real object we create, it will use the same method.
It will be discussed in prototype section.
Pros of Class
Let say we have a class as follows:
function Cat() { this.full = false}
To make it a real object, we have to call it using new
keyword as follows:
var cat = new Cat()cat// Cat {full: false}
If we pay attention to the result on the console, it returns an object that has full
property and its value is false
. We can also create a real object without using new
keyword and declaring a class. The example as follows:
var cat = { full: false}cat// {full: false}
At a glance, we get the same result. But if we take a look more closely, the browser console will add information about the class in front of the object if we create it using class, as follows Cat {full: false}
. While if we create the object directly, it won't display the class information, {full: false}
.
Class will make us easier to create a real object without redeclaring it repeatedly. Let say we have to make an object that has much property, suppose the object is human.
Human has a name, date of birth, place of birth, sex, address and many more. We also have to create many human objects.
If we declare it directly using a real object as follows:
var human1 = { // Many human property}
var human2 = { // Many human property}
var human3 = { // Many human property}
It would make us bored and there are many repetitions, isn't it? Although we can use copy and paste, it really isn't efficient.
We can save much time by declaring Human
class and if we want to create a real object, we only do it by using new
keyword.
Even because of class in JavaScript is a function, we can add parameters to it as follows:
function Human(name, dateOfBirth, placeOfBirth, sex, address) { this.name = name this.dateOfBirth = dateOfBirth this.placeOfBirth = placeOfBirth this.sex = sex this.address = address}
Using that way, we can create a different kind of human using the same class.
var human1 = new Human('Barata', '01 Januari 1995', 'Yogyakarta', 'Laki-laki', 'Lampung')var human2 = new Human('Apasari', '31 Desember 1994', 'Bandung', 'Perempuan', 'Bali')var human3 = new Human('Prabu', '15 Juli 1996', 'Bali', 'Laki-laki', 'Aceh')
human1// Human {name: "Barata", dateOfBirth: "01 Januari 1995", placeOfBirth: "Yogyakarta", sex: "Laki-laki", address: "Lampung"}human2// Human {name: "Apasari", dateOfBirth: "31 Desember 1994", placeOfBirth: "Bandung", sex: "Perempuan", address: "Bali"}human3// Human {name: "Prabu", dateOfBirth: "15 Juli 1996", placeOfBirth: "Bali", sex: "Laki-laki", address: "Aceh"}
However if we already declared the human
object directly without using class, we can change it so that it inherits the Human
class using Object.setPrototypeOf
method.
The method will set the object so that it has __proto__
property referring to prototype
property of Human
class.
It has 2 parameters, the first one is the object we wanna change and the second one is the prototype
property. The example is as follows:
var human4 = { name: "Atun", dateOfBirth: "17 Agustus 1993", placeOfBirth: "Jakarta", sex: "Perempuan", address: "Surabaya"}human4// {name: "Atun", dateOfBirth: "17 Agustus 1993", placeOfBirth: "Jakarta", sex: "Perempuan", address: "Surabaya"}
Object.setPrototypeOf(human4, Human.prototype)human4// Human {name: "Atun", dateOfBirth: "17 Agustus 1993", placeOfBirth: "Jakarta", sex: "Perempuan", address: "Surabaya"}
Besides of using Object.setPrototypeOf
method, we can also point the __proto__
property directly to refer to the prototype
property of Human
class. The example is as follows:
var human5 = { name: "Danum", dateOfBirth: "29 Februari 1997", placeOfBirth: "Dayak", sex: "Perempuan", address: "Sumba"}human5// {name: "Danum", dateOfBirth: "29 Februari 1997", placeOfBirth: "Dayak", sex: "Perempuan", address: "Sumba"}
human5.__proto__ = Human.prototypehuman5// Human {name: "Danum", dateOfBirth: "29 Februari 1997", placeOfBirth: "Dayak", sex: "Perempuan", address: "Sumba"}
Both of them lead to the same result.
On the next sections, we will more often experiment using __proto__
and prototype
and link between them.
Prototype
When we declare a function, JavaScript will add a property called prototype
to the function object.
function Cat() {}
Cat.prototype// {constructor: f}
The prototype
property is an object consists of constructor
function that points to another function.
The constructor
is the function executed when we call Cat
function.
We can check whether constructor
is Cat
function we already declared or not using this way:
Cat.prototype.constructor === Cat// true
Besides of it, the prototype
also has another hidden object called __proto__
. We can't check it using hasOwnProperty
method as well.
It points to another object. It is the prototype
property of Object
class.
We can also check whether the value of __proto__
is really the prototype
property of Object
class or not using this way:
Cat.prototype.__proto__ === Object.prototype// true
Base on that, we can draw a conclusion that each of function it will inherit Object class automatically. The inheritance between the object we made and Object
class linked through __proto__
object in the prototype
property.
Or we if we draw it to simple diagram, it will be as follows, Cat -> Object
. Cat
class inherit Object
class.
We can make use of prototype
property to solve the creation of class method in the previous section. So the method won't recreate each time we declare the object.
function Cat() {}
Cat.prototype.eat = function() { console.log('Cat eat...')}
var kucing = new Cat()kucing.eat()// Cat eat...
We can also check whether eat
method won't recreate each time we make several objects using this way:
var catA = new Kucing()var catB = new Kucing()var catC = new Kucing()
catA.eat = catB.eat// true
catA.eat = catC.eat// true
catB.eat = catC.eat// true
They are all the same method because if we compare it like the snippet above it returns true
.
In the previous snippet, we declare eat
method on the prototype
property. It can be called directly through cat
object even though if we console the cat
is an empty object.
cat// Cat {}
But if we take a look more closely, the cat
object has a hidden object called __proto__
. It contains 3 other objects, the first one is eat
method, the second one is constructor
and the last one is __proto__
points to Object
class prototype.
Although eat
method is located inside __proto__
, we can access it directly.
It is because when we access the eat
method, JavaScript will check whether cat
has eat
method or not.
If not, JavaScript will continue the search to __proto__
property, turns out that the __proto__
has eat
method, so that the JavaScript stop to search more and execute the method.
Another case if we want to execute the undeclared method, let say it is called drink
. JavaScript will do a search in cat
object, it doesn't have the method. Then JavaScript continue it's search to __proto__
, it also doesn't have.
The search is to continue to another __proto__
that inside of the previous __proto__
. And it also doesn't have.
The last __proto__
property checked by JavaScript refers to Object
class. If it doesn't have the method we wanna call, JavaScript will raise an error that the method is undefined.
We can draw the link between them in a simple diagram as follows, cat -> __proto__ (Cat) -> __proto__ (Object)
.
The first __proto__
refers to Cat
object and the second one is refers to Object
class.
Anyway, the __proto__
that the cat
object has is refers to Cat
class prototype. We can check it as follows:
cat.__proto__ === Cat.prototype// true
The link between __proto__
to other __proto__
and the prototype
to other prototype
is called prototype chaining. We can make use of it to do inheritance in JavaScript.
Inheritance
In the previous section it is discussed how inheritance process in JavaScript. It use __proto__
and prototype
. It will discuss more in this section.
Inheritance in JavaScript will divide into 2 section, inheritance for object and inheritance for class.
Object Inheritance
Let say we have cat
and bird
object as follows:
var cat = { full: false, eat: function() { console.log('Cat eat...') this.full = true }, meow: function() { console.log('Cat meow...') }}
var bird = { full: false, eat: function() { console.log('Bird eat...') this.full = true }, fly: function() { console.log('Bird fly...') }}
The cat
object has full
property, eat
and meow
method, whle the bird
object has full
property, eat
and fly
method.
Both of them has the same full
property and eat
method, we can apply inheritance to shorten the declaration.
We can combine the eating ability of them to another object, let say the object is animal
.
var animal = { full: false, eat: function() { console.log('Animal eat...') this.full = true }}
var cat = { __proto__: animal, meow: function() { console.log('Cat meow...') }}
var bird = { __proto__: animal, fly: function() { console.log('Bird fly...') }}
In that snippet, we declare first the animal object that has full
property and eat
method. Then we declare cat
and bird
object that has their __proto__
property refers to animal
object.
Using that way, the cat
and bird
object will have the ability of eating as well, we can check it by calling the eat
method in cat
and bird
object as follows:
cat.eat()// Animal eat...
bird.eat()// Animal eat...
When we check the full
property to cat
and bird
object, they don't have it. It is located inside __proto__
object.
But when we call the eat
method. Both of them will have full
property automatically. Like we have discussed in this in object context, this will refer to the object itself.
As we discussed in prototype chaining, both of them has a similar ability to eat. They get it from __proto__
property referring to animal
object. JavaScript will search sequencely whether cat
and bird
object has eat
method or not.
It turns out that they don't have. JavaScript continue the search to their __proto__
property. It refers to another object called animal
and JavaScript found it right away there. Then JavaScript will call it.
Based on that case, we can draw a conclusion that inheritance in an object can be done using __proto__
property that referring to another object.
Class Inheritance
Based on the same case, let's change the snippet in the previous section into class. We will make 3 kind of class, they are Animal
, Cat
and Bird
. The result will be as follows:
function Animal() { this.full = false}Animal.prototype.eat = function() { console.log('Animal eat...') this.full = true}
function Cat() {}Cat.prototype.meow = function() { console.log('Cat meow...')}
function Bird() {}Bird.prototype.fly = function() { console.log('Bird fly...')}
But it seems declaring method using prototype looks not so nice, perhaps it is because of the prototype declared after the function is declared. It will make the method looks outside the scope of function. Because of that, let's change that into more organized.
But before we do it, it's better to get to know about immediately invoked function expression or better known as IIFE.
If we declare a function, in order to make the code inside of it run, it's common for us to call the function.
function func() { console.log('This is function...')}
func()// This is function...
While the immediately invoked function expression/IIFE is a function that when it declared it automatically called so that the code in the body of the function is executed as well.
To declare IIFE, it starts with 2 of the opening and closing brackets, ()()
. Then inside of the first opening and closing bracket, we declare a function that we wanna executed automatically without declaring its name, (function() {})()
.
(function() { console.log('Inside of immediately invoked function expression...')})()// Inside of immediately invoked function expression...
When the snippet above is executed, the function inside of the first opening and closing bracket will be executed automatically.
The common use case for IIFE is to prevent polluting global object.
After getting to know about IIFE, let's get started to change the declaration of the class above to make it well organized.
var Animal = (function() { function Animal() { this.full = false } Animal.prototype.eat = function() { console.log('Animal eat...') this.full = true } return Animal})()
var Cat = (function() { function Cat() {} Cat.prototype.meow = function() { console.log('Cat meow...') } return Cat})()
var Bird = (function() { function Bird() {} Bird.prototype.fly = function() { console.log('Bird fly...') } return Bird})()
It seems the code above looks more compicated than the previous one, isn't it? 🤦♂️
At a glance, sure it looks more complicated. But if we stare it closely, we can understand that the class declaration is moved inside IIFE. And the IIFE returns the class.
You can take a look on the highlighted code for the class declaration.
Anyway, to make it easier to discuss inheritance, let's call the class that inherits as child class and the class that inherited as parent class.
A child class in JavaScript called inherits when:
- Has
prototype
property refers to the parent class. - The
prototype
property has aconstructor
property refers to itself. - When we create the real object from child class, it should be inherit all property and method from the parent class.
From the 3 rules above, we can make a helper function to turn a class inherits another class. More or less, the function will be like as follows:
function __extends(ChildClass, ParentClass) { Object.setPrototypeOf(ChildClass, ParentClass)
function CombinedClass() { this.constructor = ChildClass } CombinedClass.prototype = ParentClass.prototype
ChildClass.prototype = new CombinedClass()}
We call the helper function __extends
because in accordance with the object-oriented programming in the others programming language, to declare an inheritance between class, they use extends
keyword.
While the __
(2 underscores) prefix is one of the unwritten rules in JavaScript that indicate a property, object or method is only used internally.
We can take a look at the body of the helper function above, Object.setPrototypeOf
will set the prototype
property of the child class to refer to the parent class. It follows the first rule.
In order to follow the second rule, we need a new helper class that set the constructor
property to refer to the child class. After that, we also have to set prototype
property of the child class to refer to the parent class.
To check the third rule, let's change the Cat
and Bird
class to inherit the Animal
class.
var Cat = (function(_super) { __extends(Cat, _super) function Cat() { return _super.apply(this, arguments) } Cat.prototype.meow = function() { console.log('Cat meow...') } return Cat})(Animal)
Suppose we already declared the helper function and the Animal
class. In the snippet above, we add a parameter called _super
which refer to Animal
in IIFE.
The __extends
function called using Cat
as the first parameter and _super
as the second parameter.
Inside the Cat
class declaration, we return a value of apply
function call located in _super
. It accepts 2 parameters, the first one is this
context and the second one is arguments
keyword.
The arguments
keyword is a variable containing all the parameter pass into a function, in that case, the function is Cat
class.
After that, let's make the real object from Cat
class we already declared.
var cat = new Cat()
cat// Cat {full: false}
In accordance with the third rule, cat
object is a Cat
that has full
property. The full
property is coming from its parent class, which is Animal
.
The cat
object is inherit eat
method from its parent. So we can call it directly through the cat
object.
cat.eat()// Animal eat...
cat// Cat {full: true}
After calling eat
method, the value of full
property is change into true
.
The __proto__
in cat
object is referring to Animal
class in accordance with the first rule. The constructor
property inside of __proto__
property is referring to itself in accordance with the second rule.
Polymorphism
Polymorphism is the object ability to take many form. Let's get back to the Cat
, Bird
and Animal
class above. We can recognize the Cat
as a Cat
and as an Animal
.
To check it we can use instanceof
keyword. This keyword checks whether an object has the same form of we wanna check or not.
var cat = new Cat()
cat instanceof Cat// true
cat instanceof Animal// true
In the example above, because we made the cat
object from Cat
class so that the checking returns true
. It also happens when we check it with Animal
class, because of Cat
class inherit from Animal
class, it will return true
as well.
Even it also returns true
if we check it with Object
class. Because all prototype chain ends in Object
class.
cat instanceof Object// true
From that case, we can draw a conclusion that all of thing in JavaScript is an Object
except number
, string
and other primitive data type.
Encapsulation
To get a better understanding about encapsulation, imagine we consume a capsule medicine like the following image:
It makes us consuming medicine easier. By using a capsule, we can only need to consume one item instead of many medicines. That's how encapsulation it is.
From that illustration, we can conclude that encapsulation is a mechanism to hide data so that we can interact with it as a single unit.
Unfortunately, because all the property and method in JavaScript is can be accessed from anywhere when this article is written JavaScript doesn't have any way to do full encapsulation.
Even though there is some hack to do it. Eric Elliot writes a great article about Encapsulation in JavaScript.
Besides that, the next version of JavaScript is also has a syntax to apply encapsulation. When this article is written, it still in step 3 to be standardized.
Abstraction
To get better understanding what is an abstraction, let's use the previous analogy, capsule analogy. The capsule consists of several kinds of medicine that help us to defeat a specific disease.
But we never know how each of them kills the bacteria, virus and disease in our body. The only thing we know is the capsule do defeat the disease.
Another analogy we can use is aircraft. When the pilot wants to fly an aircraft, the only thing he might do is press one or two-button to turn it on and pull the wheel to fly it away.
But he never knows, how each of the machines works involved in turn the aircraft on and make it fly.
Based on 2 analogies above, we can draw a conclusion that abstraction is a general problem identification process ignoring the detail. In another programming language, abstraction achieve using the interface and abstract keyword.
Again, unfortunately, when this article is written, JavaScript still don't have any way to apply abstraction. Even though there are several hacks to do it.
If you wanna apply abstraction, You can use TypeScript. TypeScript has abstract class feature.
We can think TypeScript is a JavaScript that enhanced with many features. Even though TypeScript will convert into JavaScript, TypeScript really helps us in the development phase.
Recap
Woah, it's a long discussion. 😅 We already discussed all the things in applying object-oriented programming in JavaScript.
It starts from the discussion about what is the object, this concept is quite hard to understand, the class that is a blueprint of an object and the prototype is the basis of object-oriented programming in JavaScript.
Besides that, we also have discussed the application of 4 concepts of object-oriented programming in JavaScript. It covers inheritance, polymorphism, encapsulation and abstraction.
Although when this article is written encapsulation and abstraction are can't be fully applied using JavaScript. But there is always a way to overcome it.
Anyway, if you find this discussion is a little bit overwhelming and confusing, you can always find another shorten reading material like the one written by Lydia Hallie, JavaScript Visualized: Prototypal Inheritance.
Thanks for reading and hope you enjoy! 🙌
References
- CSS Tricks: Understanding JavaScript Constructors
- ECMAScript® 2020 Language Specification: The Reference Specification Type
- Geeks for Geeks: Prototype in JavaScript
- Mozilla Developer Network: this
- Mozilla Developer Network: Object.setPrototypeOf
- Mozilla Developer Network: Object.prototype.hasOwnProperty
- Viktor Kukurba Medium: Object-oriented programming in JavaScript #1. Abstraction
- Wikipedia: Object-oriented Programming
- Wikipedia: Immediately Invoked Function Expression