JavaScript - Polymorphism

Hello there, future JavaScript wizards! Today, we're going to embark on an exciting journey into the world of polymorphism in JavaScript. Don't worry if that word sounds intimidating – by the end of this lesson, you'll be wielding polymorphism like a pro!

JavaScript - Polymorphism

Polymorphism in JavaScript

Let's start with the basics. Polymorphism is a fancy word that comes from Greek, meaning "many forms." In programming, it refers to the ability of objects to take on multiple forms or behaviors. Think of it like a chameleon changing its color to adapt to different environments.

In JavaScript, polymorphism allows us to use a single interface to represent different types of objects. It's like having a universal remote control that can operate various devices – your TV, DVD player, and sound system – all with the same buttons.

Here's a simple example to illustrate this concept:

function makeSound(animal) {
  console.log(animal.sound());
}

let dog = {
  sound: function() {
    return "Woof!";
  }
};

let cat = {
  sound: function() {
    return "Meow!";
  }
};

makeSound(dog); // Output: Woof!
makeSound(cat); // Output: Meow!

In this example, we have a makeSound function that can work with different animal objects. Both dog and cat have a sound method, but they produce different outputs. This is polymorphism in action!

Method Overriding

One of the key aspects of polymorphism is method overriding. This occurs when a child class provides a specific implementation for a method that is already defined in its parent class.

Let's look at an example:

class Animal {
  makeSound() {
    return "The animal makes a sound";
  }
}

class Dog extends Animal {
  makeSound() {
    return "The dog barks";
  }
}

class Cat extends Animal {
  makeSound() {
    return "The cat meows";
  }
}

let animal = new Animal();
let dog = new Dog();
let cat = new Cat();

console.log(animal.makeSound()); // Output: The animal makes a sound
console.log(dog.makeSound());    // Output: The dog barks
console.log(cat.makeSound());    // Output: The cat meows

Here, we have an Animal class with a makeSound method. The Dog and Cat classes extend Animal and override the makeSound method with their own implementations. This allows each animal to have its own unique sound while still being part of the Animal family.

Examples

Let's dive deeper with more examples to really cement our understanding of polymorphism in JavaScript.

Example 1: Shape Calculator

Imagine we're building a shape calculator. We want to calculate the area of different shapes using the same method name.

class Shape {
  calculateArea() {
    return 0;
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  calculateArea() {
    return this.width * this.height;
  }
}

function printArea(shape) {
  console.log("The area is: " + shape.calculateArea());
}

let circle = new Circle(5);
let rectangle = new Rectangle(4, 6);

printArea(circle);    // Output: The area is: 78.53981633974483
printArea(rectangle); // Output: The area is: 24

In this example, we have a base Shape class and two derived classes, Circle and Rectangle. Each class implements its own calculateArea method. The printArea function can work with any shape object, demonstrating polymorphism.

Example 2: Employee Payroll System

Let's create a simple employee payroll system to further illustrate polymorphism:

class Employee {
  constructor(name, salary) {
    this.name = name;
    this.salary = salary;
  }

  calculateBonus() {
    return this.salary * 0.1;
  }
}

class Manager extends Employee {
  calculateBonus() {
    return this.salary * 0.2;
  }
}

class Developer extends Employee {
  calculateBonus() {
    return this.salary * 0.15;
  }
}

function printBonus(employee) {
  console.log(`${employee.name}'s bonus is: $${employee.calculateBonus()}`);
}

let john = new Employee("John", 50000);
let jane = new Manager("Jane", 70000);
let bob = new Developer("Bob", 60000);

printBonus(john); // Output: John's bonus is: $5000
printBonus(jane); // Output: Jane's bonus is: $14000
printBonus(bob);  // Output: Bob's bonus is: $9000

In this example, we have different types of employees with different bonus calculation rules. The printBonus function works with any employee object, showcasing polymorphism.

Benefits of using Polymorphism in JavaScript

Now that we've seen polymorphism in action, let's talk about why it's so awesome:

  1. Code Reusability: Polymorphism allows us to write more generic and reusable code. Our printArea and printBonus functions can work with any shape or employee object, respectively.

  2. Flexibility: It's easier to add new types of objects without changing existing code. We could add a Triangle class to our shape calculator without modifying the printArea function.

  3. Maintainability: Polymorphism can lead to cleaner, more organized code that's easier to maintain and extend.

  4. Abstraction: It allows us to work with objects at a higher level of abstraction, focusing on what objects do rather than how they do it.

Here's a table summarizing the key methods we've used in our examples:

Method Description
calculateArea() Calculates the area of a shape
calculateBonus() Calculates the bonus for an employee
makeSound() Returns the sound an animal makes
sound() Returns the sound an animal makes (in object literal example)

Remember, young padawans, polymorphism is like having a Swiss Army knife in your coding toolbox. It's versatile, powerful, and can make your code more elegant and efficient. Keep practicing, and soon you'll be polymorphing your way to JavaScript mastery!

Credits: Image by storyset