The Principles of Object Oriented Programming (OOP)
The four fundamental OOP principles are encapsulation, abstraction polymorphism and inheritance.
I covered the four pillars of object-oriented programming in this article, with a special focus on inheritance and polymorphism using coded examples.
First let's understand how to create classes in JavaScript
Creating Classes
Consider that you need to code a Train class.
Once you've written this class, you'll be able to create objects of the Train class by using the keyword new.
class Train {}
The constructor will be used to build properties on the future object instance of the Train class.
class Train {
constructor() {
}
}
A common method of creating objects from classes is to use the Object.create()
method
class Animal { /* ...class code here... */ }
var myDog = Object.create(Animal)
console.log (Animal)
It can also be done using the new
keyword. JavaScript automatically invokes the Object superclass when using a default or empty function Object() { [native code] } method to build the instance.
class Animal { /* ...class code here... */ }
var myDog = new Animal()
console.log (Animal)
Let's assume for the time being that each object instance of the Train class should only have the properties color
and lightsOn
when instantiated
class Train {
constructor(color, lightsOn) {
this.color = color;
this.lightsOn = lightsOn;
}
}
Now, to actually build a new instance of the Train
class,
`var myFirstTrain = new Train('red', false);`
However, this is not all that classes can offer, it's discussed further in detail in this blog.
You can also add methods to classes, and these methods will then be shared by all future instance objects of my Train
class.
NOTE: this
keyword it's the future object instance of the Train class.
Four pillars of OOP
Encapsulation
Encapsulation, in its most basic form, is the process of "hiding" a code implementation from other users so that they can "consume" it without having to understand how it operates.
"abc".toUpperCase()
I shouldn't worry or even waste time trying to understand how the toUpperCase()
method functions. Since I am aware that I have access to it, all I want to do is use it. I don't have to care about what it does in the background or even how it does it as long as it doesn't break my code, even if the underlying grammar, that is, how the toUpperCase()
method is implemented, changes.
Abstraction
Writing code in a way that will make it more generic is the essence of abstraction.
Because of how difficult it may be to distinguish between encapsulation and abstraction, these ideas are frequently misunderstood.
It helps to think of it in the following terms:
The goal of an abstraction is to deal with the concept of what you're trying to do rather than a particular presentation of that concept.
Encapsulation is the process of preventing you from seeing or caring about an implementation's internal workings.
Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common superclass, particularly when you override methods in a derived class. This is an important aspect of object-oriented design, as it enables code flexibility and reusability.
In JavaScript, method overriding is a simple form of polymorphism, where a subclass provides its own implementation of a method that is already defined in its parent class.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Cat extends Animal {
speak() {
console.log(`${this.name} meows.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
function makeAnimalSpeak(animal) {
animal.speak(); // This demonstrates polymorphism
}
const myDog = new Dog("Max");
const myCat = new Cat("Whiskers");
makeAnimalSpeak(myDog); // Output: Max barks.
makeAnimalSpeak(myCat); // Output: Whiskers meows.
In the above code example,
Method overriding: Both Dog and Cat override the speak method from Animal.
Polymorphism: The makeAnimalSpeak function treats Dog and Cat instances as objects of type Animal, demonstrating that the same method speak() behaves differently based on the object.
Another example here is:
"abc".concat("def"); // 'abcdef'
["abc"].concat(["def"]); // ['abc', 'def']
["abc"] + ["def"]; // ["abcdef"]
This means that the concat()
method is exhibiting polymorphic behaviour since it behaves differently based on the context - in this case, based on what data types I give it.
To recap, polymorphism is advantageous because it enables programmers to create objects with identical functionality, i.e., functions with identical names that operate identically. In some other areas of the OOP framework, you can, however, change some portions of the shared code or even the entire functionality.
Inheritance
Inheritance is one of the foundations of object-oriented programming.
It's a fairly straightforward idea. It functions as follows: A "thing" base class exists.
The characteristics of the base class are inherited by one or more subclasses of "things" (sometimes also referred to as the "super-class")
Other sub-sub-classes of "things" that inherit from those classes may exist.
Keep in mind that every subclass derives from its superclass. In turn, a sub-class may also be a super-class if other classes are descended from it.
class Animal { /* ...class code here... */ }
class Bird extends Animal { /* ...class code here... */ }
class Eagle extends Bird { /* ...class code here... */ }
It is possible to implement polymorphism using classes in JavaScript, by inheriting from the base class and then overridingthe inherited behavior.
To inherit from one class to a new sub-class, JavaScript provides the extends
keyword,
class Train {
constructor(color, lightsOn) {
this.color = color;
this.lightsOn = lightsOn;
}
}
class HighSpeedTrain extends Train {
}
In the code that follows, you will observe another class being coded, which is named HighSpeedTrain
and inherits from the Train class.
This makes the Train class a base class, or the super-class of the HighSpeedTrain
class. Put differently, the HighSpeedTrain
class becomes the sub-class of the Train class, because it inherits from it.
To inherit from one class to a new sub-class, JavaScript provides the extends keyword, which works as follows:
To inherit from one class to a new sub-class, JavaScript provides the extends keyword,
class HighSpeedTrain extends Train {
constructor(passengers, highSpeedOn, color, lightsOn) {
super(color, lightsOn);
this.passengers = passengers;
this.highSpeedOn = highSpeedOn;
}
toggleHighSpeed() {
this.highSpeedOn = !this.highSpeedOn;
console.log('High speed status:', this.highSpeedOn);
}
}
Notice the slight difference in syntax in the constructor of the HighSpeedTrain
class, namely the use of the super
keyword.
In JavaScript classes, super
is used to specify what property gets inherited from the super-class in the sub-class.
If you're inheriting from another class and want to use the properties and methods from the parent class, you must use super() to properly initialize the child class.
In this case, I choose to inherit both the properties from the Train super-class in the HighSpeedTrain
sub-class.
Notice that in addition to the inherited properties, you also automatically inherit all the methods that exist on the Train prototype, namely, the toggleLights(), lightsStatus()methods.
Additionally, imagine you realized that you don't like how the toggleLights() method from the super-class works, and you want to implement it a bit differently in the sub-class. You can add it inside the HighSpeedTrain
class.
class HighSpeedTrain extends Train {
constructor(passengers, highSpeedOn, color, lightsOn) {
super(color, lightsOn);
this.passengers = passengers;
this.highSpeedOn = highSpeedOn;
}
toggleHighSpeed() {
this.highSpeedOn = !this.highSpeedOn;
console.log('High speed status:', this.highSpeedOn);
}
toggleLights() {
super.toggleLights();
super.lightsStatus();
console.log('Lights are 100% operational.');
}
}
So, how did you override the behavior of the original toggleLights() method?
Well in the super-class, the toggleLights() method
toggleLights() {
this.lightsOn = !this.lightsOn;
}
You realized that the HighSpeedTrain method should reuse the existing behavior of the original toggleLights() method, and so you used the super.toggleLights() syntax to inherit the entire super-class' method.
Next, you also inherit the behavior of the super-class' lightsStatus() method - because you realize that you want to have the updated status of the lightsOn property logged to the console, whenever you invoke the toggleLights() method in the sub-class.
var trainOne = new Train('blue', false);
var highSpeedy = new HighSpeedTrain(200, false, 'green', false);
You've built the trainOne
object of the Train class, and set its color to "blue" and its lightsOn to false.
Next, you've built the highSpeedy
object to the HighSpeedTrain class, setting passengers to 200, highSpeedOn to false, color
to "green", and lightsOn
to false.
If you have any query let me know in the comment section. I'll try my best to answer them.
If you find this blog helpful, please ā¤ļø like it.
You can follow me if you wish to enhance your fundamental knowledge of JavaScript
In the coming blogs I'll be covering most asked concepts of JavaScript.
Top comments (2)
Be kind to your beginner readers. Make sure your code will compile and run properly.
You have a typo: super.toggleLigths();
Sure, thankyou will definitely work on this