Object-oriented Programming (OOP) in JavaScript 2023

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.


JSX VS HTML Differcences: Understanding the differences and why it Matters.

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