The problem

JavaScript has objects and what is called prototypal inheritance, but it does not have built-in support for many features provided by other languages. In particular, it does not have built-in support for classes and instances. Nor does it have built-in support for methods of one class to call super-class methods.

Here is an example. Class C is a subclass of class B, and both have an init method. We would like the init method of Class C to be able to call the init method of class B. In other words, when writing init for class C, we wish to call the super-class init method. Note that class B might in fact be a subclass of class A, and that B might inherit init from A, and not have an init method of its own.

This post of focussed in the single topic of providing access to super-class. It does not for instance cover how to provide support for class hierarchies and how to manage instance creation.

Motivation

Over the past few days I’ve been trying to understand John Resig‘s post on Simple JavaScript Inheritance, on both the technical and the practical levels. John’s goal was to extract the soul of the implementations of classical inheritance in base2 and Prototype, and to present them as a stand-alone package.

Well, I’ve not succeeded, but I do understand the problem better. In particular, I couldn’t get a clear understanding of what his _super method did. So to help me understand I set out to write my own. And this is what I came up with.

I hope in a later post to compare the two approaches. Be warned that this post is unavoidable quite technical, and that if you’re a JavaScript novice you’ll learn a lot if you manage to understand it all.

Setting up inheritance

First we have some code that sets up A, B and C, which are what we can think of as classes, and also c, which we can think of as an instance of C. (This way of thinking is not shared with the computer. So far as the program is concerned, the relationship between B and C is exactly the same as that between C and c. But to our mind one is subclass and the other is instance.)

var fromProto = function(proto){
    var f = function(){
        this.proto = proto;
    };
    f.prototype = proto;
    return new f();
};

// 'Classes':  A <- B <- C
var base = {};
var A = fromProto(base);
var B = fromProto(A);
var C = fromProto(B);

// Test object - 'Instance': C <- c.
var c = fromProto(C);

The function fromProto creates an object whose prototype object is ‘proto’. It also records that fact. (There are other, implicit, ways in which this fact may be recorded. But for clarity, here we will make it explicit.) I learnt about this way of doing things from Douglas Crockford’s book JavaScript: The Good Parts.

Setting up methods

So that we can test our implementation, we will create some methods. We will give A methods getA and setA that get and set an ‘a’ attribute on the object. Similarly, we will give B methods getB and setB (that this time get and set a ‘b’ attribute). Once we have done this, c.getA for example will access the A method, because of the usual operation of prototypal inheritance.

To that we really are fetching super-class methods, we will add to C spoiler methods getA, setA, getB and setB. When called, these spoiler will raise an error. We will also add spoilers getB and setB to A.

To shorten and clarify the code, we introduce a couple of factory functions, for creating get and set methods.

var _get = function(key){
    return function(){
        return this[key];
    };
};

var _set = function(key){
    return function(value){
        this[key] = value;
    };
};

And now we can very simple set up the access methods and the spoilers.

A.getA = _get('a');
A.setA = _set('a');

B.getB = _get('b');
B.setB = _set('b');

// Spoilers - will not be called.
C.getA = C.setA =
    C.getB = C.setA =
    A.getB = A.setB =
    function(){
        ddt();
    };

Low-level access to super-class methods

Here we use only JavaScript primitives to access super-class methods. We don’t define and use functions of our own (we’ll do that later). In fact, the code below tells up what primitive commands the functions we define must call.

We wish to call the method A.getA, but with the special this object being the object c. If it were not for C have the spoiler method C.getA, we could write c.getA() to call A.getA with this being c. But the spoiler is there, so we have to do something else.

JavaScript has apply and call methods precisely for dealing with this problem. They allow us to specify the object that is to be bound to this during the method call. More exactly (and this has to be fully understood to follow the rest of this post) both of the function calls below:

    fn.apply(that, [arg0, arg1, arg2, ...])
    fn.call(that, arg0, arg1, arg2, ...)

are equivalent to

    fn(arg0, arg1, arg2, ...)

except that in this function call this is bound to the value of that.

This, apply and call are exactly what we want. Moreover, JavaScript does not provide any other method for doing this. We are obliged to use apply or call. We have not choice here, except which to use. (We’ll have more choice later, in how to package our call in functions of our own devising.)

Using call to access super-class methods

We can now write and run our first test function.

C.test1 = function(){

    // Understanding proto.
    C.proto === B || ddt();

    // Here's how to call set.
    C.proto.setA.call(this, 'a1');
    C.proto.setB.call(this, 'b1');

    // Check that set worked.
    this.a = 'a1' || ddt();
    this.b = 'b1' || ddt();

    // Call get, and check that it worked.
    C.proto.getA.call(this) === 'a1' || ddt();
    C.proto.getB.call(this) === 'b1' || ddt();
};

c.test1();

The code above runs without error. Note that we use C.proto to access the inheritance chain, rather than use our knowledge that C.proto is in fact B.

Why we need a sweeter syntax

The above code works, and is perhaps optimal for execution time. (If as is likely we knew that C.proto.getA was not going to change, we could do this calculation outside the method body and use a cached result to make it run quicker.)

But it far from optimal for programmer productivity. There are several aspects to this:

  • It is not immediately obvious that we are calling a super-class method.
  • The code is tricky to write.
  • The code depends on an implementation detail – the use of proto to hold the prototypal object.
  • The code contains a gotcha – omit call and it silently fails.

Some more on the gotcha. The line of code

    C.proto.setA(this, 'a1');    // Gotcha! You forgot the call!

first resolves C.proto to B, and then B.setA to the function A.setA. It then calls A.setA with arguments the value of this (which is the test object c) and the string ‘a1’. And now for the gotcha. In this function call, this is bound to the previous value of this (and the argument ‘a1’ is ignored). Therefore, in this particular case, the above line of code has the same effect as

    B.a = c;

which is not at all what is wanted.

We don’t want traps like this lying so close to the code we are writing. We want to focus, so to speak, on the road rather than the potholes. We need a sweeter syntax. (And should performance be an issue, we can later unwind the sweeter syntax into the primitive operations presented earlier.)

A sweeter syntax

Here we have some choices. We wish to sweeten the previous syntax. Let’s recall what we have

    C.proto.setA.call(this, 'a1');

Now we have to choose. My choice is

    sup(C, 'setA')(this, 'a2');

Notice that sup(C, ‘setA’) is a drop-in replacement for C.proto.setA.call. This is why I like it. (I chose ‘sup’ because we can’t use ‘super’, which is a reserved word in JavaScript. It is one of the many reserved words in JavaScript, that are not actually used in the language.)

Although this is my present choice, I hope that I’m open to persuasion and learning from experience. I also believe that going along with the majority is sometimes (not always) the best thing for the community. So if the choice of syntax matters to you, please speak up.

High-level access to super-class methods

We’ll now rewrite the low-level test using the sweeter syntax. Here it is:

C.test2 = function(){

    // Here's how to call set.
    sup(C, 'setA')(this, 'a2');
    sup(C, 'setB')(this, 'b2');

    // Check that set worked.
    this.a === 'a2' || ddt();
    this.b === 'b2' || ddt();

    // Call get, and check that it worked.
    sup(C, 'getA')(this) === 'a2' || ddt();
    sup(C, 'getB')(this) === 'b2' || ddt();
};

Before we can run this test, we need to implement the ‘sup’ function.

Implementation of sup

Recall that

    sup(C, 'setA')(this, 'a2');

is to be equivalent to

    C.proto.setA.call(this, 'a1');

Here’s the definition of sup. I find it the hardest piece of code to understand in this post. I think you have to see it first, and then work through understanding it.

var sup = function(klass, name){
    return function(){
        var fn = klass.proto[name];
        return Function.call.apply(fn, arguments);
    };
};

So let’s go through it, step by step.

  1. The sup function returns a function. This is good. We will be calling this function with, say, c and ‘a1’ as arguments. For now, let’s call this function supSetA.
  2. The function supSetA has set up a closure (we won’t explain here what that is) and as a result klass and name have within supSetA the values that were originally supplied. (By the way, ‘class’ a reserved word in JavaScript that is not used, just like ‘super’. So we use ‘klass’ instead.)
  3. Executing supSetA sets fn to the function A.setA, exactly as before. (We don’t move that calculation outside the definition of the returned function. Our focus is on programmer productivity, and we can’t be sure that we’re able to safely cache this value.)
  4. We now execute Function.call.apply(fn, arguments), which is so complicated we’ll give its explanation all by itself.

The Function.call.apply pattern

I’m fairly confident that I’m the first person to publish code that uses this pattern. (To check, I did this Internet search and also this one for the string ‘Function.call.apply’. At the time of writing, they produced 19 hits, none of which coincide with our use.) So if you’re an early reader, you might be the second person on earth to know about this pattern. Don’t be surprised if takes you a little while – it took me about an hour!

Let’s unpack the pattern. We have

    Function.call.apply(fn, arguments);

Here goes:

  1. Recall that myfunc.apply(obj, [arg1, arg2, arg3, …) will execute myfunc(arg1, arg2, arg3, …), but with this set equal to obj.
  2. In our case we have
    1. myfunc is call.
    2. obj is the function A.setA.
    3. arguments is [c, ‘a2’].
  3. So now we have call(c, ‘a2’), but with this set equal to C.proto.setA (which is equal to A.setA).
  4. Recall that previously we used the low-level code C.proto.setA.call(this, ‘a2’) where this is c.
  5. Now for the hard bit. In (4), we are running call(c, ‘a2’), but with this set equal to C.proto.setA. (Please take some time to think about this. It follows from the rules as to how this is determined.)
  6. Now for the easy bit. Steps 3 and 5 are identical, and so give the same result. This is what we wanted to see.

Conclusion

This has been a long and technically demanding post. As stated earlier, I hop to discuss the relation to other approached in another post.

I’m optimistic that most of the ideas in this post will make their way into at least one future JavaScript classical inheritance framework.

At the same time, I’m bearing in mind Douglas Cockcroft’s warning:

I have been writing JavaScript for 8 years now, and I have never once found need to use an uber function. The super idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns. I now see my early attempts to support the classical model in JavaScript as a mistake.

My view (based on about 18 months experience of JavaScript) is that there’s room for improvement in the way in which we handle inheritance. I learned a lot about JavaScript when authoring this post, and I hope that it will also help others.

Advertisements