dcl.superCall()
Version 2.x
dcl.superCall()
is a light-weight way to call a method with the same name from the base “class”, if any. Essentially
it is a way to call a method that was overridden by the current method. It is used as a method
decorator.
It is defined as a property on dcl
.
Description
When you need to do a supercall, you decorate/wrap a method with dcl.superCall()
using a double function pattern:
1 2 3 4 5 |
|
The outer function always have one argument: sup
(any name is fine). It is used to inject a method of super class
in the inner function, which does the useful job. The outer function always returns the inner function.
The inner function takes as many arguments as required and returns the actual value. It is the workhorse of the tandem. In doing its work it can optionally call its super method.
It is worth noting that sup
is an unadorned super method. Most probably you want to call it in context of a current
object. Do not forget to use standard apply()
or call()
methods to supply an object, and/or arguments:
1 2 3 4 5 6 7 8 9 10 |
|
It is possible that there is no super method to call (e.g., this “class” is the first one in line). In this case the
injected sup
will be falsy. It is a good idea to check sup
for presence.
1 2 3 4 5 6 7 8 9 10 |
|
The reason to use the double function pattern is described in Supercalls in JS.
Transitioning from a regular method to a method, which can execute a supercall is very simple:
1 2 3 |
|
In the example above we transition a method called method
, which is implemented by a function called X
.
Let’s transition it to a supercalling method:
1 2 3 4 5 |
|
As you can see our X
function is completely preserved — all arguments are the same, its code, and its return
value is completely intact. We just added a decorator and a trivial wrapper function that returns X
. Now our method
can take advantage of a supercall, which is injected using sup
argument of the wrapper.
Example
More complete example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
In the example above B
can always expect that A
comes before it, and method()
in B
always have a super method,
meaning that checking for sup
to be truthy/falsy was unnecessary. But even in trivial cases like in the example above
possible modifications/refactoring may subtly change your assumptions making the check necessary.
Notes
- If a method is present in an
Object
, it will be the last in line of potential super calls. - It is not necessary to call a super method. This decision can be made dynamically.
- The funky double function pattern allows for static chaining of super calls. In this context “static” means “once at
constructor definition”.
- It makes super calls as cheap as possible. No extra expenses per call.
- It makes debugging simple: going inside a super call brings a programmer directly to the next method without any stubs, thunks, or wrapper functions.
- Even if a programmer failed to check if
sup
is truthy and called it anyway, JavaScript will generate an exception pointing directly to the site of failure without any intermediaries. - Both functions should be unique and created dynamically exactly like in examples.
- If a super method throws an exception, it is a programmer’s responsibility to catch it, to ignore it, or to pass it through.
FAQ
Is it possible to call built-in functions like that?
Yes.
1 2 3 4 5 6 7 8 |
|
Is it possible to use a super call in a chained method?
Yes. In this case, if a super is not called, a chain is interrupted. It is one of the methods to interrupt chained constructors. Think twice before interrupting construction chains — usually it is a bad idea.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
The same can be done with any chained method. See dcl.chainBefore() and dcl.chainAfter() for more details.