Next: Visibility Escalation, Previous: Overriding Methods, Up: Inheritance [Contents]
2.2.5 Type Checks and Polymorphism
The fact that the API of the parent is inherited is a very important detail. If the API of subtypes is guaranteed to be at least that of the parent, then this means that a function expecting a certain type can also work with any subtypes. This concept is referred to as polymorphism, and is a very powerful aspect of Object-Oriented programming.
Let’s consider a dog trainer. A dog trainer can generally train any type of dog (technicalities aside), so it would stand to reason that we would want our dog trainer to be able to train LazyDog, AngryDog, TwoLeggedDog, or any other type of Dog that we may throw at him/her.
Type checks are traditionally performed in JavaScript using the
instanceOf
operator. While this can be used in most inheritance cases
with ease.js, it is not recommended. Rather, you are encouraged to use
ease.js’s own methods for determining instance type7. Support for the
instanceOf
operator is not guaranteed.
Instead, you have two choices with ease.js:
Class.isInstanceOf( type, instance );
Returns
true
if instance is of type type. Otherwise, returnsfalse
.Class.isA( type, instance );
Alias for
Class.isInstanceOf()
. Permits code that may read better depending on circumstance and helps to convey the “is a” relationship that inheritance creates.
For example:
var dog = Dog() lazy = LazyDog(), angry = AngryDog(); Class.isInstanceOf( Dog, dog ); // true Class.isA( Dog, dog ); // true Class.isA( LazyDog, dog ); // false Class.isA( Dog, lazy ); // true Class.isA( Dog, angry ); // true // we must check an instance Class.isA( Dog, LazyDog ); // false; instance expected, class given
It is important to note that, as demonstrated in Figure 2.20 above, an instance must be passed as a second argument, not a class.
Using this method, we can ensure that the DogTrainer may only be used with an instance of Dog. It doesn’t matter what instance of Dog - be it a LazyDog or otherwise. All that matters is that we are given a Dog.
var DogTrainer = Class( 'DogTrainer', { 'public __construct': function( dog ) { // ensure that we are given an instance of Dog if ( Class.isA( Dog, dog ) === false ) { throw TypeError( "Expected instance of Dog" ); } } } ); // these are all fine DogTrainer( Dog() ); DogTrainer( LazyDog() ); DogTrainer( AngryDog() ); DogTrainer( TwoLeggedDog() ); // this is not fine; we're passing the class itself DogTrainer( LazyDog ); // nor is this fine, as it is not a dog DogTrainer( {} );
It is very important that you use only the API of the type that you
are expecting. For example, only LazyDog and AngryDog implement
a poke()
method. It is not a part of Dog’s API.
Therefore, it should not be used in the DogTrainer class. Instead, if
you wished to use the poke()
method, you should require that an
instance of LazyDog be passed in, which would also permit
AngryDog (since it is a subtype of LazyDog).
Currently, it is necessary to perform this type check yourself. In future versions, ease.js will allow for argument type hinting/strict typing, which will automate this check for you.
Footnotes
(7)
The reason for this will become clear in future chapters. ease.js’s own methods permit checking for additional types, such as Interfaces.
Next: Visibility Escalation, Previous: Overriding Methods, Up: Inheritance [Contents]