What is Polymorphism

Understanding the Metaphor

Let’s look at this picture of a peacock and peahen. The two birds have somewhat different features:

  • The peacock has a large fan tail, the peahen doesn’t;
  • The peahen lays eggs, the peacock doesn’t…
This phenomenon is called “sexual dimorphism”, another example is how body size diverges in gorillas depending on sex. In biology, morphology is the study of form or shape or appearance or structure or anatomy. The Greek root ‘-morph-’ means ‘form’. “Dimorphism” refers to the “two different forms”.

Yet, they are subclasses of the same species. Both developed from almost identical eggs. In fact, it used to be so difficult to discern the sex of baby chickens (they look the same in pretty much every way), it was a lucrative specialized profession, and the technique was some heavily guarded trade secret, taught in “chicken sexing schools”

Lab coat-clad Chicken sexing specialists in training.

Now let’s think about honeybees. The different types of honeybees all come from eggs produced from the queen bee, but they develop into subclasses of bees. Since there are many types (instead just two), the phenomenon is termed “polymorphism”, where ‘poly-’ means ‘many’.

The subclasses of honeybees have different features specific to each type, but they also share common features, such as flying.

The types or subclasses (Worker vs. Drone) have their own special features and behaviors, but they also share a common interface (properties and methods, such as ‘flying’) inherited from the superclass (Honeybee).

The WorkerBee has specific genetic material (functional definitions) that gets expressed which turns it into a worker bee, and enables it to perform worker bee related tasks, however, it “inherits” the basic flying behavior (common to all honeybees) from the ‘Bee’ superclass.

Defining a Subclass

var Kitten = function () {
// apply the initialization stuff in the superclass
Cat.apply(this, arguments)
}
// Meanwhile, in the superclass
var Cat = function () {
// initialization stuff
this.age = 0;
this.color = 'orange';
// ...
}

The superclass also has behavior definitions that are stored as method properties in its ‘prototype’ property:

Cat.prototype.meow = function () {
console.log('Meow!');
}

When you initialize a new cat, it has all the initialized properties; it also has all the class behaviors defined in the prototype:

var garfield = new Cat();
console.log(garfield.age);   // 0
console.log(garfield.color); // 'orange'
garfield.meow();             // 'Meow!'

Prototype Delegation

When we create a subclass, we want to set up a “prototype chain” so that methods on the superclass is made available through upstream look-up.

For example:

Kitten.prototype = Object.create(Cat.prototype);

When we call a method on an instance of kitten:

var fluffy = new Kitten();
fluffy.meow();

Since the Kitten class doesn’t have its own definition of .meow, the look-up will search the upstream of the prototype chain, and look under the Cat class to see if Cat.prototype has that method. In our case, the Cat class indeed has the .meow method. Because all cats can meow and all kittens are from a subclass of Cat and fluffy is an instance of Kitten —fluffy can meow.

To make Fluffy know that it is an instance of the more specific Kitten class, we set the constructor property on the Kitten prototype to refer to itself:

Kitten.prototype.constructor = Kitten;

Then when you ask what fluffy’s constructor is, it points to the Kitten class, rather than the broader superclass that it inherited from (Cat).

fluffy instanceof Kitten
// true

Some Conceptual Details

  • Cat.prototype is a property of Cat
  • You can point that property to a value in memory
  • That value can be an object
  • You can set another variable or property to point to that same object
  • No matter how many times you designate and re-assign such a reference, each assignment is still pointing to the same value/object in memory.

Let’s imagine the memory as a large warehouse, and the variables or property names are like tags or labels. Each tag or label shoots off a very long string, that attaches to a box somewhere in the warehouse on the other end. You can remove the attachment from that box, and attach it to a different box, or you can attach multiple strings and labels to the same box; but you don’t store the box in the label, and the label is not the box.

When you call the Object.create method, something different happens. The method creates a new box somewhere in the warehouse.

And if you say:

var myLabel = Object.create(Box);

The tag myLabel is now stringed to that new instance of Box.

When you say:

Kitten.prototype = Object.create(Cat.prototype);

The Object.create factory manufactures a new object in memory that resembles Cat.prototype (by delegating up the chain), and sets up the property Kitten.prototype to point to that new object.

Old Behavior and New Behavior

If you want a subclass method to be largely based on a superclass method, but also does some additional stuff on top, you can call that superclass method from within the subclass method’s definition:

Dog.bark = function () {
// specific dog-specific barking behavior
this.sound = "Woof!"
// common sound-making behavior
Mammal.phonate.apply(this, arguments);
};
// Meanwhile, in the super-class function:
Mammal.phonate = function () {
console.log(this.sound);
}
// The meowing extension follows a similar pattern:
Cat.meow = function () {
this.sound = "Meow!"
Mammal.phonate.apply(this, arguments);
};

In the above example, the Dog.bark method and the Cat.meow method set the custom content that is specific to Dog or Cat. Both methods then ‘import’ the superclass method Mammal.phonate to log that content.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.