JavaScript Object-Oriented Programming: Method Overriding

JavaScript Object-Oriented Programming: Method Overriding

In object-oriented programming, method overriding is a powerful feature that allows a subclass or child object to provide a specific implementation of a method that is already defined in its parent or superclass. This means that when the method is called on an instance of the subclass, the subclass’s version of the method runs instead of the parent’s. Overriding enables programmers to change or extend the behavior inherited from a parent class, making objects more flexible and specialized without changing the parent code.

JavaScript supports method overriding in multiple ways, thanks to its flexible prototype-based inheritance system and modern class syntax. Understanding method overriding is crucial to mastering how inheritance works in JavaScript, as it allows different objects to behave uniquely even when they share the same method names. This article will guide you step-by-step through practical examples of method overriding in JavaScript, starting with ES6 classes and then moving to constructor functions, prototypes, and object literals.

Method Overriding with ES6 Classes

ES6 introduced a clean and modern way to define classes in JavaScript, making it straightforward to override methods. Let’s start by defining a simple parent class called Vehicle with a method named move.

class Vehicle {

  move() {
    console.log("The vehicle moves forward.");
  }

}

class Car extends Vehicle {

  move() {
    console.log("The car drives on the road.");
  }

}

const myVehicle = new Vehicle();
myVehicle.move(); // Output: The vehicle moves forward.

const myCar = new Car();
myCar.move(); // Output: The car drives on the road.

In this example, the Vehicle class has a method move() which prints a generic message. The subclass Car overrides this method with its own version. When move() is called on myCar, the overridden method runs, showing the specialized behavior for cars. This is a clear demonstration of how method overriding works with ES6 classes.

Calling the Parent Method Inside the Overridden Method

Sometimes, you want the child class method to extend the behavior of the parent rather than completely replacing it. JavaScript provides the super keyword to call the parent class’s methods inside an overridden method.

Here’s how it works:

class Vehicle {

  move() {
    console.log("The vehicle moves forward.");
  }

}

class Car extends Vehicle {

  move() {
    super.move();
    console.log("The car drives on the road with style!");
  }

}

const myCar = new Car();
myCar.move();
// Output:
// The vehicle moves forward.
// The car drives on the road with style!

In this case, Car overrides the move() method but calls super.move() first to execute the original Vehicle method. Then it adds its own message. This pattern is helpful when you want to keep the base behavior while adding extra functionality.

Method Overriding with Constructor Functions and Prototypes

Before ES6, JavaScript used constructor functions and prototypes to achieve object-oriented behavior. Method overriding is still possible here by redefining methods on the prototype of the child constructor.

Consider this example with animals:

function Animal() {}

Animal.prototype.speak = function() {
  console.log("The animal makes a sound.");
};

function Dog() {}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() {
  console.log("The dog barks.");
};

const genericAnimal = new Animal();
genericAnimal.speak(); // Output: The animal makes a sound.

const myDog = new Dog();
myDog.speak(); // Output: The dog barks.

Here, Animal has a speak() method defined on its prototype. Dog inherits from Animal by setting its prototype chain, then overrides speak() with a new implementation. When speak() is called on a Dog instance, the overridden method is executed, showing the polymorphic behavior.

Overriding Methods in Object Literals Using Object.create()

JavaScript’s prototype system allows objects to inherit directly from other objects, not just from classes or constructors. You can override methods by creating objects with a prototype and redefining methods in the child object.

Let’s see an example with shapes:

const Shape = {

  draw() {
    console.log("Drawing a shape.");
  },

  test() {
    console.log("Test method.");
  }

};

const Circle = Object.create(Shape);

Circle.draw = function() {
  console.log("Drawing a circle.");
};

Shape.draw(); // Output: Drawing a shape.
Shape.test(); // Output: Test method.

Circle.draw(); // Output: Drawing a circle.
Circle.test(); // Output: Test method.

Here, Circle inherits from Shape. The draw() method is overridden in Circle by redefining it. Calling draw() on Circle runs the new method, while Shape.draw() remains unchanged.

Fun Example: Overriding Methods in a Game Character Hierarchy

To make the concept more engaging, imagine a game with different character classes. We will create a base Character class with an attack() method. Each subclass will override attack() to perform unique actions.

class Character {

  attack() {
    console.log("The character attacks!");
  }

}

class Wizard extends Character {

  attack() {
    console.log("The wizard casts a fireball!");
  }

}

class Knight extends Character {

  attack() {
    console.log("The knight swings a sword!");
  }

}

class Rogue extends Character {

  attack() {
    console.log("The rogue strikes from the shadows!");
  }

}

const characters = [new Wizard(), new Knight(), new Rogue()];

characters.forEach(character => character.attack());

// Output:
// The wizard casts a fireball!
// The knight swings a sword!
// The rogue strikes from the shadows!

This example shows how method overriding lets each character type define its own attack style. When we loop over the array and call attack(), each subclass’s overridden method runs, demonstrating polymorphism through overriding.

Overriding Built-in Methods

JavaScript allows overriding built-in object methods like toString(). This can make objects more descriptive when logged or converted to strings.

Here’s an example with a Book class:

class Book {

  constructor(title, author) {
    this.title = title;
    this.author = author;
  }

  toString() {
    return `"${this.title}" by ${this.author}`;
  }

}

const myBook = new Book("The Lion King", "Arthur");
console.log(myBook.toString()); // Output: "The Lion King" by Arthur
console.log(String(myBook));    // Output: "The Lion King" by Arthur

By overriding toString(), the Book instances provide meaningful string representations instead of the default [object Object]. This enhances how objects display in logs or user interfaces.

Conclusion

Method overriding in JavaScript is a key tool for creating flexible, dynamic objects that share common behavior but specialize or extend it where needed. Whether using modern ES6 classes, traditional constructor functions, or prototype-based objects, overriding lets subclasses replace or extend parent methods cleanly. This allows for rich object hierarchies, polymorphic behavior, and customization of built-in methods, all essential concepts in object-oriented programming with JavaScript.

References

If you want to learn more about method overriding and JavaScript inheritance, these resources are excellent starting points:

Scroll to Top