Next: Type Checks and Polymorphism, Previous: Understanding Member Inheritance, Up: Inheritance [Contents]
2.2.4 Overriding Methods
When a method is inherited, you have the option of either keeping the parent’s implementation or overriding it to provide your own. When you override a method, you replace whatever functionality was defined by the parent. This concept was used to make our LazyDog lazy and our TwoLeggedDog walk on two legs in Figure 2.15.
After overriding a method, you may still want to invoke the parent’s method.
This allows you to augment the functionality rather than replacing it
entirely. ease.js provides a magic __super()
method to do this. This
method is defined only for the overriding methods and calls the parent
method that was overridden.
In order to demonstrate this, let’s add an additional subtype to our hierarchy. AngryDog will be a subtype of LazyDog. Not only is this dog lazy, but he’s rather moody.
var AngryDog = Class( 'AngryDog' ).extend( LazyDog, { 'public poke': function() { // augment the parent method console.log( 'Grrrrrr...' ); // call the overridden method this.__super(); } } ); // poke a new AngryDog instance AngryDog().poke(); // Output: // Grrrrrr... // Woof!
If you remember from Figure 2.16, we added a
poke()
method to LazyDog. In Figure 2.17 above, we are
overriding this method so that AngryDog growls when you poke him.
However, we still want to invoke LazyDog’s default behavior when he’s
poked, so we also call the __super()
method. This will also make
AngryDog bark like LazyDog.
It is important to note that __super()
must be invoked like any other
method. That is, if the overridden method requires arguments, you must pass
them to __super()
. This allows you to modify the argument list before
it is sent to the overridden method.
2.2.4.1 Arbitrary Supertype Method Invocation
The aforementioned __super
method satisfies invoking an overridden
method within the context of the method that is overriding it, but falls
short when needing to invoke an overridden method outside of that context.
As an example, consider that AngryDog
also implemented a
pokeWithDeliciousBone
method, in which case we want to bypass the
dog’s angry tendencies and fall back to behaving like a LazyDog
(the
supertype). This poses a problem, as we have overridden LazyDog#poke
,
so calling this.poke
would not yield the correct result (the dog
would still respond angerly). __super
cannot be used, because that
would attempt to invoke a supermethod named
pokeWithDeliciousBone
; no such method even exists, so in this case,
__super
wouldn’t even be defined.
We can remedy this using this.poke.super
, which is a strict reference
to the overridden poke
method (in this case, LazyDog.poke
):
var AngryDog = Class( 'AngryDog' ).extend( LazyDog, { 'public poke': function() { // ... }, 'public pokeWithDeliciousBone': function() { // invoke LazyDog.poke this.poke.super.call( this ); } } ); // poke a new AngryDog instance with a delicious bone AngryDog().pokeWithDeliciousBone(); // Output: // Woof!
It is important to note that, in its current implementation, since
super
is a reference to a function, its context must be provided
using the ECMAScript-native apply
or call
(the first argument
being the context); using this
as the context (as shown above) will
invoke the method within the context of the calling
instance.6
Footnotes
(6)
Specifically, it will invoke the method within the context of the calling instance’s private visibility object (see The Visibility Object). While this may seem like a bad idea—since it appears to give the supermethod access to our private state—note that the method wrapper for the overridden method will properly restore the private state of the supertype upon invocation.
Next: Type Checks and Polymorphism, Previous: Understanding Member Inheritance, Up: Inheritance [Contents]