Polymorphism
Corbin Callais
Posted on July 9, 2020
Introduction
Polymorphism
is a term used with constructor function instantiations to give multiple functions a tree of sorts, each sharing the previous's properties and methods.
It's primarily used to cut down on code in Object-Oriented Programming to make sure the working experience is streamlined, giving a "write less, do more" attitude if you will.
While it's a simple concept on the surface, it isn't uncommon to see a newer coder get stuck on the "coding magic" that is polymorphism, and even instantiation as a whole. This blog will, by the end of it, help you on your way to making all kinds of constructors for all kinds of things.
How does it work?
When we instantiate a constructor function, we primarily have two choices in syntax in JavaScript1: ES5
and ES6
. ES5
is more familiar to most coders, as it doesn't take away any of the syntax that has been since JavaScript's creation. ES6
is functionally identical, but it adds a lot of syntactic sugar to make it much more convenient to look at.
For the examples, we will be using ES5
syntax.
For ES5
, when we want to call what's called the superclass
, or its "parent" class, We do this with the conveniently-named .call(this)
. this
is called for the context, as we want the constructor itself to be instantiated with the parent class. Also don't forget to pass in any relevant arguments your subclass needs defaulted by the superclass!
Also keep in mind that because we call the superclass on the subclass itself, that means the prototype
is also copied. Make sure to copy that with Object.create(<superclass>.prototype)
to the proper constructor name before you move on. Same goes with the prototype.constructor
specifically. Remember, you copied the proto, so you should make sure all names are relevant to their context.
// Superclass declaration
const Auto = function(owner) {
this.owner = owner;
};
Auto.prototype.drive = function() {
/* {...} */
};
// Subclass declaration
const Car = function(make, model, owner) {
// Calling the superclass, Auto, with .call(this), also passing in the owner param.
Auto.call(this, owner);
this.make = make;
this.model = model;
};
// Copying the proto...
Car.prototype = Object.create(Auto.prototype);
// Changing the constructor function. This is important for when the call stack needs
// to refer back to something. As with everything, you should ALWAYS keep information
// relevant.
Car.prototype.constructor = Car;
ES6
however, doesn't need to do that whole Object.create()
thing after the base function. In fact, because ES6
has completely different syntax, you do things just as differently. When you define your constructor() {}
, you start by calling the superclass with the aptly named super()
function, once again passing in the relevant parameters.
On top of that, instead of doing <superclass>.call(this, ...args)
, to define what the superclass is, you use yet another keyword that ES6
added in, that being extends
. You place it after your class name, but before the code block.
// Superclass
class Auto {
constructor(owner) {
this.owner = owner;
}
drive() {
/* {...} */
}
}
// Subclass
// Notice how we add "extends Auto" after the normal naming.
class Car extends Auto {
constructor(make, model, owner) {
// super(owner) is basically <superclass>.call(this, owner). In this case,
// <superclass> is Auto.
super(owner);
}
// And we don't need anything else. "extends" does that jumble of mess below the
// base for us.
}
And if you're feeling extra brave, know that subclasses can also have their own subclasses, same rules applied as before. This makes a "tree" of call chaining, calling the more and more general parent classes to get back all of the properties that should be owned by ALL subclasses, or to hardcode certain parameters, depending on what you're trying to do.
class Car extends Auto {
constructor(make, model, owner) {
super(owner);
this.make = make;
this.model = model;
}
}
class FordCar extends Car {
// Notice how the parameters for the constructor get shorter the more hardcoded things
// you enter.
constructor(model, owner) {
super('Ford', model, owner);
this.model = model;
}
}
class FordFocus extends FordCar {
constructor(owner) {
super('Focus', owner);
}
}
// This could go on for a while, but you get the idea.
/*
And in case you need a bit more of a visual...
FordFocus('James') is calling
FordCar('Focus', 'James') which is calling
Car('Ford', 'Focus', 'James').
*/
Conclusion
Polymorphism
is a fairly simple concept primarily in Object-Oriented Programming used to create a "tree" of constructors, to cut down on the code required to write, which seems menial in small examples like these, but can be a lifesaver in much bigger projects. And understanding this concept thoroughly allows you to make your code cleaner, shorter, and with much less hassle than if you were to do it separately.
Superscript References
- JavaScript is not a primarily Object-Oriented Programming language, and similarly, the term
polymorphism
is not reserved to it. It's a concept that's found in languages such as Python, C/#/++, and Java, who focus more heavily on OOP given their structure.
Posted on July 9, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024