OOPS in JavaScript with easy to understand examples π₯π₯
Nehal Mahida
Posted on November 21, 2021
Introduction
In this article, we will learn and use Object-Oriented Programming (OOP) concepts in javascript.
If you are coming from an Object-Oriented Programming language like C++ or Java, You probably wished to use class-based syntax in javascript.
If you don't know about OOP, don't worry I am going to explain you all the concepts with easy to understand examples.
Nice to have
Before the release of ES6, We were using Constructor functions to use the OOP concept. But now with, the help of ES6 Classes, we can have more similar syntax like C++ or Java to use the OOP concept.
(Behind the scene ES6 Classes uses the same constructor functions)
That's cool π
But what is Object-Oriented Programming? π€
Here is a popular definition of OOP
Object-Oriented Programming(OOP) is a programming paradigm based on the concepts of Objects.
Means? π
That means everything that we achieve in OOP language is through objects here object defines the real-world entities like Student or Car (More detail soon).
Ok! But why do we need OOP?
Good Question!
The main goal of developing OOP was organising the structure of the code. Using OOP, you can write more modular and maintainable code. You can associate the code with real-world entities.
By using OOP, you make sure that only allowed members of one code is accessible to others. That makes your code fully secured to unauthenticated access (within the code).
Get it??
Now, Let's see the main concepts of Object-Oriented Programming step by step.
Object
As I already mentioned above, Objects are like real-life entities. They have their properties and methods.
Consider a car as an object. The car has so many characteristics like colour, company name, modal name and price, etc. On a car, we can perform actions like start, break, and stop. Here characteristics of a car are properties, and actions are methods.
If you are using javascript for a while, you may use objects many times in your code but maybe not in an OOP way.
Let me create one user object here.
const user = {
name: 'Nehal Mahida',
userName: 'nehal_mahida',
password: 'password:)',
login: function(userName, password) {
if (userName === this.userName && password === this.password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
},
};
user.login('nehal', 'nehal');
user.login('nehal_mahida', 'password:)');
// Authentication Failed!!
// Login Successfully
The above code is pretty self-explanatory. I am creating one user object having some properties and actions that he can perform.
Nothing new, right??
Let's understand some more OOP concepts.
Class
Class is a blueprint of a real-life entity. It describes how the object will look alike, what characteristics it holds and what kind of actions we can perform on it.
Class is just a template. You can't perform any actions on it. Consider class is your website UX Design(wireframes). You create it to get an idea of how your website UI will look alike at the end. Users can't do interactions with your wireframes as they will do on an actual website.
We instantiate the object from a class. We can create many instances of a class.
Let's take an example.
class User {
#password;
constructor(name, userName, password) {
this.name = name;
this.userName = userName;
this.#password = password;
}
login(userName, password) {
if (userName === this.userName && password === this.#password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
setPassword(newPassword) {
this.#password = newPassword;
}
};
const nehal = new User('Nehal Mahida', 'nehal_mahida', 'password:)');
const js = new User('JavaScript', 'js', 'python:)');
nehal.login('nehal_mahida', 'password:)'); // Login Successfully
js.login('js', 'python:)'); // Login Successfully
console.log(nehal.name); // Nehal Mahida
console.log(nehal.password); // undefined
console.log(nehal.#password); // Syntax Error
nehal.setPassword('new_password:)');
nehal.login('nehal_mahida', 'password:)'); // Authentication Failed!!
nehal.login('nehal_mahida', 'new_password:)'); // Login Successfully
Here I have created a class named User
, which has some properties and methods. Then I am creating instances of the class using new User()
and passing the values of required properties.
Did you see one constructor
method which we never called in our code??
Actually, The method has been called π
When we create an object from a class using the new
keyword javascript internally calls the constructor method which initialised the public and private properties of a class. The object here can access all the public properties and methods of a class.
What is public
and private
properties??
By default, all the properties declared in the class are public means you can call and modify them from outside the class. You can declare public properties in or out of the constructor. Here name
and userName
are public properties.
What about private?
Again look at the code. Did you notice the password is written outside of the constructor method prefixed with #
?
Hash(#
) indicates that this property is private to the class and only methods that are declared inside the class can access it. Private properties should be declared before they were used.
When I tried to print the password, I got undefined
as I don't have any member named as 'password', then I tried it with '#password' that gave me a syntax error because the '#password' is private.
To print/modify the private properties, we need getter/setter methods. Here I have created one method that set the new password.
The following concepts are the four pillars of OOP langueage.
Encapsulation
Encapsulation is defined as binding the data and methods into a single unit to protect it from outside access. Just like a pill contains medication inside of its coating.
In the context of class, some properties are not directly accessed from outside of the class. You need to call the responsible method for the properties.
Sounds familiar?
Yes, You guess it right. It's like creating a getter/setter method for the private properties we declare in a class.
In the above example, we already used encapsulation. We bind(logically) private property password
with a public method setPassword()
. You also have one getter method, which returns the current value of a private property.
Abstraction
People often misunderstood encapsulation with abstraction. Abstraction is one step ahead of encapsulation. Abstraction is defined as showing only the essential things and hiding the inner implementation.
Let's take an example of a car. On a Car, we can perform some actions like start, break and stop. Whenever you call one of these actions, it gives you some result. These actions have certain sub-actions which are hidden from you, but you don't need to care about those sub-actions.
This is how car company uses an abstraction of functionality to give their customer a smooth experience.
Let's take another example of abstraction. Suppose you are using some third-party react component for your front-end project. This component provides many props and methods for your customisation. This component is no magic it internally uses the same HTML tags, CSS and javascript. But now you don't need to worry about those things. You just need to set props and call methods based on your requirements. That's an abstraction.
Let's code π€©
class User {
name;
email;
#password;
constructor() {}
#validateEmail(email) {
// check email is valid or not.
return true;
}
#validatePassword(password) {
// check password is satisfying the minimum requirements or not.
return true;
}
signUp(name, email, password) {
let isValidated = false;
isValidated = this.#validateEmail(email);
isValidated &&= this.#validatePassword(password);
if (isValidated) {
this.name = name;
this.email = email;
this.#password = password;
// add user in your db.
console.log('User registered successfuly');
} else {
console.log('Please enter correct Details!!');
}
}
login(email, password) {
if (email === this.email && password === this.#password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
#isRegisteredUser(email) {
// check user is registered or not.
return true;
}
resetPassword(email, newPassword) {
if (this.#isRegisteredUser(email)) {
this.#password = newPassword;
console.log('Operation performed successfully');
}
else {
console.log('No account found!');
}
}
};
const nehal = new User();
nehal.signUp('Nehal Mahida', 'nm@gmail.com', 'password:)'); // User registered successfuly
nehal.#validateEmail('nm@gmail.com'); // Syntax Error.
nehal.login('nm@gmail.com', 'password:)'); // Login Successfully
nehal.resetPassword('nm@gmail.com', ''); // Operation performed successfully
In the above example, we have introduced some private methods. The methods are doing some work and they are not exposed to the outside of the class.
These methods are called by the publically available methods.
As a developer, I just need to give the details I received from the UI and call the responsible method.
In OOP languages like Java, we have a concept of abstract classes and interfaces. That's not possible in javascript.
Otherwise, we can create one abstract class and that class can be used by another class to achieve similar functionality.
So basically we can say we are using encapsulation to achieve abstraction. π
Inheritance
When one class derived the properties and methods of another class it is called inheritance in OOP. The class that inherits the property is known as subclass or child class and the class whose properties are inherited is known as a superclass or parent class.
Why do we need inheritance?
Inheritance is a very important concept in OOP. The main advantage of inheritance is reusability. When a child class inherits from parent class we don't need to write the same code again. It becomes very reliable when we need to do some change in properties just change it in a parent class and all the child classes will automatically inherit the change. Inheritance also promotes code readability.
Let's code...
class User {
#password;
constructor(email, password) {
this.email = email;
this.#password = password;
}
login(email, password) {
if (email === this.email && password === this.#password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
resetPassword(newPassword) {
this.#password = newPassword;
}
logout() {
console.log('Logout Successfully');
}
}
class Author extends User {
#numOfPost;
constructor(email, password) {
super(email, password);
this.#numOfPost = 0;
}
createPost(content) {
// add content to your DB. :)
this.#numOfPost++;
}
getNumOfPost() {
return this.#numOfPost;
}
}
class Admin extends User {
constructor(email, password) {
super(email, password);
}
removeUser(userId) {
// remove this userId from your DB.
console.log('User Removed successfully.');
}
}
const nehal = new Author('nm@gmail.com', 'password:)');
nehal.login('nm@gmail.com', 'password:)');
nehal.createPost('I hope you are enjoying this article. Don\'t forget to leave your feedback. :)');
nehal.createPost('I am tired, Do you wanna buy me a coffee? :)');
console.log(nehal.getNumOfPost()); // 2
const json = new Admin('jason@gmail.com', '[Object] [object]');
json.login('jason@gmail.com', '[Object] [object]');
json.resetPassword('{id: 1}');
json.login('jason@gmail.com', '{id: 1}');
json.removeUser(12);
In the above example, the Author
and Admin
classes inherit the property of the User
class using extends
and super
keywords.
The extends
keyword is used to establish a parent-child relationship between two classes. In the first case, the Author
becomes sub-class and the User
becomes parent class.
Sub-class has access to all the public and protected members of a superclass. In addition, It can have its own properties and methods. This is how we can achieve reusability through inheritance.
The super
keyword is a special keyword. Calling super
in the child's constructor invokes the parent constructor. That's how we are initialising the properties in the Author
and Admin
classes.
The child class can also override the methods of a parent class. This introduces the concept of polymorphism.
Polymorphism
Polymorphism means 'more than one form'. Like us, We the software engineers can work on the frontend, backend, DevOps and even testing. π
Polymorphism has two types.
- Compile time Polymorphism
- Runtime Polymorphism
Function overloading is a type of compile-time polymorphism. Here, we are creating more than one function with the same name and different parameters or types.
Function overloading is not supported in JavaScript because if you create functions with the same name, Javascript will override the last defined function with former functions.
Method overriding is a type of runtime polymorphism. Remember I told you that you can override the methods of parent class in the child class? That is method overriding.
Let's take example.
class User {
constructor(email, password) {
this.email = email;
this.password = password;
}
login(email, password) {
if (email === this.email && password === this.password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
}
class Author extends User {
#numOfPost;
constructor(email, password) {
super(email, password);
this.#numOfPost = 0;
}
createPost(content) {
// add content to your DB. :)
this.#numOfPost++;
}
getNumOfPost() {
return this.#numOfPost;
}
}
class Admin extends User {
constructor(email, password) {
super(email, password);
}
login(email, password) {
// add extra layer of security as this is an admin account.
const isValidAdmin = true; // we can have some 2FA type security check here.
if (email === this.email && password === this.password && isValidAdmin) {
console.log('Admin Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
removeUser(userId) {
// remove this userId from your DB.
console.log('User Removed successfully.');
}
}
const nehal = new Author('nm@gmail.com', 'password:)');
nehal.login('nm@gmail.com', 'password:)'); // Login Successfully
const json = new Admin('jason@gmail.com', '[Object] [object]');
json.login('jason@gmail.com', '[Object] [object]'); // Admin Login Successfully
Here, the Author
and Admin
both inherit the User
class. Both classes have the login
method of the User class. Now I need some extra level of verification for the admin account, so I have created a login method in the Admin class. It will override the parent's login
method.
When an object of the Admin
class calls the login
method, it will invoke a function call to the login
method of the Admin
class.
This is how we have achieved polymorphism using method overriding.
That's it. We have covered all the concepts of OOP with JavaScript. π€©
Note: All the information above is based on my knowlege and research. If you find anything wrong here, please correct me in the comment section. Happy Learning π
If you like this article like share and mark π this article!
If you are on Twitter, give a follow, I share amazing resources to learn web development. ππ»
The feedbacks are appreciated. π€
πββοΈ Let's Connect π
π Twitter (See you on Twitter π)
π¨βπ» Github
π Support
If you are enjoying my articles, consider supporting me with a coffee.β
Posted on November 21, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.