DEV Community

loading...
Cover image for What is Polymorphism? - Explaining Polymorphism to a Five Year Old

What is Polymorphism? - Explaining Polymorphism to a Five Year Old

Kingsley Ubah
Web Developer. Technical Writer. Educator. Freelancer. African in Tech. Sign up to my newsletter below.
Originally published at ubahthebuilder.tech ・4 min read

In class-oriented languages, a class is simply an organized code which acts as a template or blueprint for creating objects. A parent class can also be ‘copied’ to create a child class. You can think of the parent (super) and child (sub) classes in the same way you would think of the relationship between a real-life parent and its child. Additionally, you could see it as the relationship between more abstract concepts or objects with more specializaed ones, like a superset and subset.

When a child is born, the characteristics of the parent are copied into the child. In other words, the child inherits behaviours from the parent. This is known as class inheritance in class-oriented languages. Let’s see some code illustrations:


// Abstract Class: An Aircraft
class Aircraft {
  constructor(wings = 2, engines = 2, wheels = 3) {
    this.wings = wings;
    this.engines = engines;
    this.wheels = wheels
  }
  // Start the engine
  start() {
    console.log(`turning on my ${this.engines} engines!`);
  }
  // Stand 
  stand() {
    console.log(`standing on my ${this.wheels} tires!`);
  }

  // Run on the tracks
  run() {
    console.log(`moving down the runway on my ${this.wheels} tires!`);
  }

// Fly in the sky
 fly() {
    console.log(`soaring the sky on my ${this.wings} wings!`);
   }
}

// A Child Class: A Helicopter
class Helicopter extends Aircraft {
  constructor(engines, wheels, propellers) {
    super(engines, wheels); // call the super class constructor and pass in the engines and wheels parameter
    this.propellers = propellers;
  }

  propel() {
    console.log(`spinning my ${this.propellers} propellers!`);
  }

  lift() {
    this.stand();
    this.start();
    this.propel();
    console.log('slowly climbing up thanks to my spinning propellers!');
  }
}

// Another Child Class: An Aeroplane
class Aeroplane extends Aircraft {
  constructor(wings, engines, wheels, passengers) {
    super(engines, wheels); // call the super class constructor and pass in the name parameter
    this.passengers = passengers;
  },
  start() {
    console.log(`turning on my ${this.engines} balanced engines!`);
  },

  takeOff() {
    this.stand();
    this.start();
    this.run();
    this.fly();
    console.log(`all of the ${this.passengers} passengers are screaming in delight!`);
  }
}
Enter fullscreen mode Exit fullscreen mode

We define the Aircraft class to assume two wings, two engines, three wheels, a way to start it's engine, stand, run and also a way to fly. But in real-life cases you wouldn’t ever manufacture just a generic “aircraft,” so it’s really just an abstract concept at this
point. Hence, then we define two specific kinds of aircrafts: a helicopter and an aeroplane.

They each inherit some of the general characteristics of an aircraft, but then each of them tailor the characteristics appropriately for themselves. A helicopter needs
two propellers and has to propel while taking off, and a aeroplane is assumed to need three engines because it’s actually conveying lots of passengers.

Polymorphism implies the existence of a property or method across two or more classes in various levels of the inheritance chain. It involves the referencing of a property or method from classes higher on the inheritance hierarchy.

In most class-oriented languages, including ES6 class, this is achieved by calling the super() keyword along with the name of the property of method you want to access, from any method in the child class. When you do this, the JavaScript engine looks one step up into a parent class for that method.

Now, let us consider one interesting implication of polymorphism in our class examples. The start() method was defined in both the Helicopter class and the Aeroplane class. When you define a method of same name in both parent class and child class, then you are said to have overridden that of the parent class. Now if you observed well, you would notice that the takeoff() method made a reference to the start() which, as I said, exists in both parent and child. Which version of start() do you think the JavaScript engine will use? Is it the one in Aircraft or the one in Aeroplane

This brings us to one vital thing to always keep in mind about polymorphism:

  • When you make a reference to any property or method from a subclass, the engine first checks to see if that method already exists in the current subsclass. If it exists, the engine uses that method. If it doesn’t exist, the engine “looks up” the next (parent) class for it, and that goes on.

Now, to answer the question, the JavaScript engine will use the Aeroplanes version of start(), which will resolve to 3 engines since a passenger plane has 3:

const plane = new Aeroplane(2, 3, 3, 117)
console.log(plane.takeOff()) 

/* turning on my 3 balanced engines!
   Standing on my 3 wheels
   Moving down the track on my 3 tires 
   Soaring the sky on my 2 wings
   all the 117 passengers are screaming in delight */

Enter fullscreen mode Exit fullscreen mode

If we had instantiated the generic Aircraft class instead, then its version of
start() would have been called instead:

const aircraft = new Aircraft() // Nothing passed  because we had set default parameters

console.log(aircraft.start())
// turning on my two engines!
Enter fullscreen mode Exit fullscreen mode

True to its name, polymorphism allows you define and use properties and methods in many forms. In our case, The start() method has two forms, and the form which gets used depends on the class which gets instantiated.

Polymorphism is a very useful feature of Object oriented programming. You can create one single interface which you can reuse in multiple subclasses. You can save yourself time and write far less code.

When it comes to polymorphism and inheritance in JavaScript, there is an important behaviour to understand: the [[Prototype]] Mechanism. This is how the engine traverses upwards looking for the existence of a property or method on the superclasses, up until it reaches the prototype object.

What is the [[Prototype]] mechanism? We’ll leave that for our next episode of the Explain like I’m 5 series.

YOU MAY ALSO LIKE:

P/S: If you like articles like this follow this blog to never miss an update. If you are learning JavaScript, you’ll definitely want to check out my JavaScript Notes.

Discussion (7)

Collapse
lukeshiru profile image
LUKESHIRU • Edited

Just to be clear, you can achieve "polymorphism" without classes in JavaScript, so is not like you need classes to have polymorphism. Your example reimplemented without classes could look something like this:

const createAircraft = ({ wings = 2, engines = 2, wheels = 3 } = {}) => ({
    start: () => console.log(`turning on my ${engines} engines!`),
    stand: () => console.log(`standing on my ${wheels} tires!`),
    run: () => console.log(`moving down the runway on my ${wheels} tires!`),
    fly: () => console.log(`soaring the sky on my ${wings} wings!`)
});

const createHelicopter = ({ engines = 1, wheels = 0, propellers = 2 } = {}) => {
    const aircraft = createAircraft({ engines, wheels });
    const propel = () => console.log(`spinning my ${propellers} propellers!`);
    const lift = () => {
        aircraft.stand();
        aircraft.start();
        propel();
        console.log("slowly climbing up thanks to my spinning propellers!");
    };

    return { ...aircraft, propel, lift };
};
Enter fullscreen mode Exit fullscreen mode

Cheers!

Collapse
machineno15 profile image
Tanvir Shaikh • Edited

i understood some part of this, can you please explain me what's happening here ? that will be helpful

Collapse
lukeshiru profile image
LUKESHIRU • Edited

Sure!

  • First: createAircraft takes a single argument, which is an object with the values, but you can pass some and omit others, or omit them all and leave the defaults if you want. The good thing about it being an object, is that you can easily skip some arguments, or use whatever order you prefer, and it still works. Adding new arguments is a piece of cake as well.
createAircraft(); // Leaving all the defaults valid
createAircraft({ wings: 1 }); // Setting only 1 property valid as well
createAircraft(({ wings = 1, engines = 1, wheels = 4 }); // Setting all properties... valid!
Enter fullscreen mode Exit fullscreen mode
  • Second: Because createAircraft returns an object with some functions in it, you can create new objects from it, so when createHelicopter does { ...aircraft }, is adding all the functions returned by createAircraft to its own output object, and then some more (propel and lift).
  • Third: This example could be way more simple if the functions were outside of createAircraft, but the idea was to show that you can "replicate" what you can do with classes, without them.
  • Forth: Ideally, every method should actually return the string instead of logging it, but I tried to replicate the original example.

Bonus: Here's how you can use the code above, and what's the output:

createAircraft();
/*
Returns:
{
    start: () => console.log(`turning on my 2 engines!`),
    stand: () => console.log(`standing on my 3 tires!`),
    run: () => console.log(`moving down the runway on my 3 tires!`),
    fly: () => console.log(`soaring the sky on my 2 wings!`)
}
*/
createHelicopter({ propellers: 2 });
/*
Returns:
{
    start: () => console.log(`turning on my 2 engines!`),
    stand: () => console.log(`standing on my 3 tires!`),
    run: () => console.log(`moving down the runway on my 3 tires!`),
    fly: () => console.log(`soaring the sky on my 2 wings!`),
    propel: () => console.log(`spinning my 2 propellers!`),
    lift: () => {
        aircraft.stand(); // Will log `turning on my 2 engines!`
        aircraft.start(); // Will log `standing on my 3 tires!`
        propel(); // Will log `spinning my 2 propellers!`
        console.log("slowly climbing up thanks to my spinning propellers!");
    };
}
*/
Enter fullscreen mode Exit fullscreen mode

Let me know if there is something else I can help you with :D

Collapse
ubahthebuilder profile image
Kingsley Ubah Author

Thanks for the insight.

Collapse
snickdx profile image
Nicholas Mendez

Because you can't typecast helicopter objects as aircraft objects, I'd argue your example cannot apply polymorphism in the true sense of the term. Inheritance is the key a was shown in the OP while what you showed composition.

Collapse
lukeshiru profile image
LUKESHIRU

You can use helicopter in any place you use aircraft, because they have the same methods (as noted by ...aircraft). You can see it working in TS as well if you want, here. It is closer to composition, but the comment mainly highlights that polymorphism is not something that you can only achieve with classes.

Collapse
tarekali profile image
Tarek Ali

Such a great explanation. Thanks.