Object-oriented Programming (OOP) in JavaScript 2023
What is Object-oriented Programming (OOP)?
Object-Oriented Programming(OOP) is a programming paradigm that revolutionizes the way we organize and structure code. JavaScript, a versatile language, fully embraces OOP concepts, empowering developers to create robust and scalable applications. In this article, we will drive into the fundamental principles of OOP in JavaScript, accompanied by illustrative examples that will bring these concepts to life.
What is OOP in simple words? Object-oriented programming is based on the concept of objects. (Source: Funtech)
JavaScript Objects: The Building Blocks
In JavaScript, an object is a powerful construct that combines a collection of properties, each represented by a key-value pair. The keys are strings that serve as property names, while the values can be of any data type, including other objects
In JavaScript, objects are king. If you understand objects, you understand JavaScript. (w3school)
let’s consider an example of an object in JavaScript:
const person = {
name: 'Miraz Hossain',
age: 39,
greet: function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
};
person.greet(); // Output: Hello, my name is Miraz Hossain and I am 39 years old.
In this example, we create a person
object with name
and age
property, as well as greet
method that outputs a friendly greeting to the console.
To access object properties, we can utilize either dot notation or bracket notation:
console.log(person.name); // output: 'Miraz Hossain'
console.log(person['age']); // output: 39
Classes: Blueprint for Objects
Classes provide a blueprint for creating objects with similar properties and behaviors. In JavaScript, you can define classes using the class
keyword. Let’s take a look at an example:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
In this case, we define a Person
class with a constructor that takes two arguments name
and age
. The constructor initializes two properties of the object name
age
. Additionally, we define a greet
method that outputs a greeting to the console, utilizing the name
and age
properties.
To create an object of the Person
class, we utilize the new
keyword:
const person = new Person('Miraz', 39.5);
person.greet(); // output: Hello, my name is Miraz and I am 39.5 years old.
Inheritance: Reusing Code
Inheritance empowers the creation of new classes based on existing ones. The new classes are based on existing ones. The new class, Known as the subclass, inherits the properties and methods of the existing class, known as the superclass.
JavaScript implements inheritance through the extends keyword. Let’s consider an example:
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
this.grade = grade;
}
study() {
console.log(`${this.name} is a ${this.grade} in JavaScript.`);
}
}
In this example, we define a Student
class that extends the Person
class, The Student
class has its own constructor, accepting three arguments: name
age
and grade
. The constructor invokes the super method, which in turn calls the constructor of the Person
class and initializes the name
and age
properties. Furthermore, the Student
class introduces a study
method, which outputs a message to the console.
To create an object of the Student class, we again employ the new
keyword:
const student = new Student('Tony Stark', 52, 'Pro');
student.greet(); // Output: Hello, my name is Tony Stark and I am 52 years old.
student.study(); // output: Tony Stark is a Pro in JavaScript.
We observe that the Student
class inherits the greet
method from the Person
class, while also introducing its own study
method.
Encapsulation: Data Hiding
Encapsulation involves the practice of concealing internal object details and offering a public interface for interactions. This prevents external code from directly modifying the object’s internal state, effectively reduce bugs and other issues. JavaScript manipulating closures to achieve encapsulation. Let’s examine an example:
function createCounter() {
let count = 0;
return {
increment() {
count++;
},
decrement() {
count--;
},
getCount() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.getCount()); //Output: 0
counter.increment();
console.log(counter.getCount()); //Output: 1
counter.decrement();
console.log(counter.getCount()); //Output: 0
Pro Tip: Don't forget to use comma after every method.
In this example, we define a createCounter
function that returns an object with three methods increment
decrement
and getCount
. The count variable is declared within the createCounter
function and remains inaccessible from outside the object. The increment
and decrement
methods modify the count variable, while the getCount
method retrieves its current value.
Polymorphism: One Interface, Many Implementations
Polymorphism refers to the ability of objects belonging to different classes to be treated as if they were objects of the same class. JavaScript embraces polymorphism through interfaces.
Consider the following example:
class Shape {
draw() {
console.log('Drawing shape...');
}
}
class Circle extends Shape {
draw() {
console.log('Drawing circle...');
}
}
class Square extends Shape {
draw() {
console.log('Drawing square...');
}
}
function drawShapes(shapes) {
shapes.forEach(shape => {
shape.draw();
});
}
const shapes = [
new Circle(),
new Square(),
new Circle(),
new Square()
];
drawShapes(shapes);
In this example, we define a Shape
class with a draw
method. Additionally, we define Circle
and Square
classes that extend the Shape
class, each overriding the draw
method with their unique implementations.
We further define a drawShapes
function, which accepts an array of Shape
objects and invokes their respective draw
methods. We create an array containing Circle
and Square
objects, passing it to the drawShapes
function. Remarkably, despite the objects belonging to different classes, they can be treated as if they were instances of the Shape
class.
An Example Combining the Powers
To solidify our understanding of objects, classes, inheritance, and polymorphism in JavaScript, let’s explore an illustrative example:
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} is eating`);
}
sleep() {
console.log(`${this.name} is sleeping`);
}
repeat() {
console.log('Just Repeat And Do It Again Until Done');
}
}
class Cat extends Animal {
constructor(name) {
super(name);
}
meow() {
console.log(`${this.name} says meow.`);
}
sleep() {
console.log(`${this.name} is sleeping`);
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
brak() {
console.log(`${this.name} says woof`);
}
eat() {
console.log(`${this.name} is eating Nuts`);
}
}
let animals = [
new Cat('Tom'),
new Dog('Tommy'),
new Cat('Anna'),
new Dog('Dollar')
];
for (let animal of animals) {
animal.eat();
animal.sleep();
if (animal instanceof Cat) {
animal.meow();
}
if (animal instanceof Dog) {
animal.brak();
}
}
In this comprehensive example, we define a base class called Animal
with a constructor method that takes a name
parameter, setting it as an instance variable. The Animal
class also boast three methods eat
sleep
repeat
Subsequently, we define two subclasses Cat
Dog
. Both classes inherit from Animal
by utilizing the extends
keyword. They each have their own constructor methods that call super
to invoke the parent class constructor and assign the name
instance variable.
The Cat
class introduces a meow
method that outputs a message to the console. Additionally, it overrides the sleep
method inherited from the parent class. On the other hand, the Dog
class features a bark
method that logs a message to the console. It also overrides the eat
method inherited from the parent class.
Subsequently, we create an array of Animal
objects, encompassing both Cat
and Dog
instances. We iterate through the array, invoking the eat
and sleep
methods on each object. Additionally, we utilize the instanceof operator to identify if an object belongs to the Cat
or Dog
class, subsequently calling the appropriate method( meow
for Cat objects and bark
for Dog objects).
Through this comprehensive example, we witness how objects, classes, inheritance, and polymorphism harmoniously combine in JavaScript to produce reusable and extensible code.
Concluding Thoughts
In this article, we take off on an exploration of essential object-oriented programming (OOP) concepts in JavaScript. We covered objects, classes, inheritance, encapsulation, and polymorphism, delving into their definitions and implementation examples. Armed with this knowledge, we are equipped to write maintainable and reusable JavaScript code, enhancing the quality of our applications. Happy coding!
“Don’t be a programmer, Be a problem Solver” - Anonymous