Introduction to Polymorphism
Polymorphism could be defined as an object's ability to manifest in different forms. The abstract notion of polymorphism manifest as the concrete application of subclassing. A class works great when you want to create a fleet on similar objects. Polymorphism allows us to subclass in order to augment different behavior onto different objects.
Functional Inheritance
Functional inheritance can be used with functional instantiation (aka: Factory Pattern) or shared functional instantiation. Functional inheritance is when you instantiate an object inside a function, and then augment that object with special properties before returning it.
function Shape(width, height) { var shape = {}; shape.width = width; shape.height = height; return shape; } function Square(width, height) { var shape = Shape(width, height); shape.size = this.width * this.height; return shape; } square = Square(100, 100); >>>Object {width: 100, height: 100, size: 10000}
We called the Shape
factory and received a Shape
object in return. We then
augmented it with a size
property and returned the augmented
object.
Pseudo-classical Inheritance
Pseudo-classical inheritance is used with pseudo-classical instantiation
(aka: Constructor Pattern). There are two different things that need to be
inherited when using the pseudo-classical style. Firstly, you need to inherent
properties from within the constructor. Secondly, you need to inherent
properties from ConstructorName.prototype
. Additionally, you also need
to manually set the subclass's constructor
property. The subclass
inherents it's prototype from the superclasses prototype, this works fine but it
has the unfortunate side effect of destroying the reference the subclasses
prototype constructor property has to itself. We need to set the subclasses
constructor property manually as a
result.
function Fruit(sweetness, freshness, organic) { this.sweetness = sweetness; this.freshness = freshness; this.organic = organic; } function Apple(sweetness, freshness, organic) { Fruit.call(this, sweetness, freshness, organic); this.color = "red"; this.name = "apple"; }
Notice when we use pseudo-classical inheritance that the line
Fruit.call(this, sweetness, freshness, organic);
is what is doing the
heavy lifting here. All we need to do is call the superclass Fruit
from within subclass Apple
for inheritance to occur. The reason we
do not need to set any variables explicitly is because our call
to
Fruit
is occurring in the context of Apple
. Therefore
when this
is used in superclass Fruit
it is actually
referring to the object that is being instantiated in Apple
since
this
refers to the object being created when using the Constructor
pattern.
As stated previously, on top of calling Fruit.call(this, sweetness, freshness,
organic);
we also need to set inheritance in their Prototype chains
correctly and set the subclasses constructor property manually on it's
prototype.
Apple.prototype = Object.create(Fruit.prototype); Apple.prototype.constructor = Apple
If we do not manually set Apple.prototype.constructor = Apple
then the constructor
property will be lost due to inheritance
overriding it. We would then get weird behavior
when using functionality like instanceof
.