Inheritance in JavaScript

In the last post, we saw how to implement class-like objects in JavaScript, despite the language’s lack of actual class data types. The solution used closure to create private fields, which can be either data or functions. Once you’ve got classes up and running, it’s natural to ask if it’s possible to simulate some of the other OOP concepts in JavaScript. Probably the most important of these is inheritance.

After poking about a lot and failing to find a convincing implementation of inheritance, I chanced upon a rather concise post by Steffen Rusitschka. He offers two ways of implementing inheritance, but although his code is fairly easy to use, he really doesn’t explain how it works, and that’s far from obvious. I’ll try to provide a bit of an explanation here.

I’ll look only at the closure-based version, since it follows on nicely from my last post.

At the heart of the technique is this helper code:

(function(){
  CClass = function(){};
  CClass.create = function(myConstructor) {
    var k = this;
    c = function() {
      this._super = k;
      var pubs = myConstructor.apply(this, arguments), self = this;
      for (key in pubs) (function(fn, sfn) {
        self[key] = typeof fn != "function" || typeof sfn != "function" ? fn :
          function() { this._super = sfn; return fn.apply(this, arguments); };
      })(pubs[key], self[key]);
    };
    c.prototype = new this;
    c.prototype.constructor = c;
    c.extend = this.extend || this.create;
    return c;
  };
})();

To use this code, we must first create the data types and then create objects from them. As a test, we will use these data types:

var X = CClass.create(function () {
  return {
    x: 3,

    test: function () { return 2 * this.x; }
  };
});

var Y = X.extend(function () {
  return {
    y: 4,

    test: function () { return this._super() + this.x + this.y; }
  };
});

var xx = new X;
var yy = new Y;

The X class (we’ll call them classes even though JavaScript doesn’t have classes as such; it’s easier to write about that way) has a single data field and a simple function which returns twice the data field. The Y class inherits X, adds a second data field, and overrides the test() function. Note that Y’s test() calls a function called _super(), which calls the base class version of test(). (It’s called ‘_super’ rather than ‘super’, since ‘super’ is a reserved word in JavaScript, even though it doesn’t do anything (yet).)

Once we’ve created the two classes, we can create objects from them in the usual way, using the ‘new’ operator. Let’s try to trace through this code to see what it’s doing.

First, note that the helper code at the top is actually run (notice the parentheses on line 18 at the bottom, which cause the preceding function to be run). Thus we create CClass as an empty function on line 2, and then define the create() function in the remaining code.

Now we look at the declaration of the X class. This is done by calling CClass.create() with the given function as the argument, so this function becomes myConstructor in the helper code. Since CClass is calling create(), ‘this’ refers to CClass, which is an empty function. This is saved in the variable k.

Now a function named c is defined. We’ll look at what it does in a minute, but note that at the end, its prototype is defined as ‘new this’, so the prototype gets a copy of CClass; also the constructor is set to the function c itself. On line 15, an extend() function is defined as either ‘this.extend’ if it’s defined or, if not, ‘this.create’. On the first call to create(), extend() won’t be pre-defined, so on this pass, extend is set to create and becomes the same function as create().

Also, note that the variables k and myConstructor as used inside the c function so, by closure, the values they had when the function was created are saved and are available to the c function when it is used later on.

The result of defining the X class is then to assign to X the c function produced by CClass.create(). Before we move on to Y, let’s see what happens when we create a new object using X, as we do on line 35.

This code treats X like a constructor, so the c function we just created gets called, and its ‘this’ is the object being created. Looking back at the first code block, we see that on line 6, this new object has its _super() defined as k, which you recall was set as the CClass earlier, and which is still available because of closure. Thus here, _super() becomes the empty function CClass.

On line 7, myConstructor() is called using JavaScript’s built-in apply() method, which specifies the calling object as the first argument (which is ‘this’ here) and then passes any arguments as an array (this will be empty in our simple example here). Remember that myConstructor contains the function defined on lines 19 to 25, so the result of this is that pubs contains a new object defined using that function.

Also on line 7, a ‘self’ variable is declared, and set to ‘this’. Since we haven’t actually created any data fields in our new object yet, ‘self’ at present is empty. The idea is to copy the fields from pubs to self so that the object created by constructor call (which is not pubs, since that was created by calling a different function) has the same fields as pubs.

The for loop on line 7 does this. It loops over each key in the pubs object. The body of the loop is another of those self-calling functions. Looking at line 11, we see that the arguments passed to the function are pubs[key] and self[key]. Since self is empty, all these elements will be undefined at this point. This means that the condition on line 9 ‘typeof sfn != “function”‘ will be true for every key, with the result that self[key] gets ‘fn’ assigned to it for every key. Since fn is the first function argument, this effectively runs self[key] = pubs[key], and copies pubs into self as required.

Thus the c function is a sort of roundabout way of implementing a constructor. However, the fun starts when we implement inheritance. On line 27, we create a Y class that inherits X by calling X.extend(). As before, the argument to extend() is a function which acts as a constructor for Y. When we created X, remember that extend() got assigned to create(), so calling X.extend() really calls X.create(). The process is similar to before, except that ‘this’ on line 4 now refers to X rather than CClass. The c function assigned to Y contains the same code as that assigned to X, but the values of k and myConstructor are those in force when X.extend() was called, using closure.

The main differences happen on lines 13 to 15. The prototype is defined as ‘new this’, which means it gets a copy of X. Remember that the prototype of a constructor is what’s the __proto__ of an object created from that constructor points to, so new objects created from this constructor will have all the fields of X.

On line 15, this.extend does exist now so it gets assigned to c.extend.

Now let’s see what happens if we create a Y object, as on line 36. The ‘new’ operator creates a new object by first pointing this object’s __proto__ at the constructor’s prototype. Remember we set Y’s prototype to an X object, so this means that the new Y object yy will inherit the fields of X right at the start. That is, ‘this’ will effectively b an X object at the start of the construction process, so ‘self’ gets assigned to an X object on line 7.

How about pubs, also on line 7? It is initialized by calling myConstructor, which contains the function on lines 27 to 33. This code knows nothing about X, so pubs contains only those fields specified in the Y constructor. Here, we have 2 fields: y and an override of test().

The for loop on line 8 runs for these 2 fields (but not for x, since that’s not mentioned in the Y constructor). For the y field, self[‘y’] is undefined, so it gets assigned to pubs[‘y’] and gets the value 4. However, when we look at the ‘test’ key, self[‘test’] does exist since there was a test() defined in X. In this case both conditions on line 9 are false (both fn and sfn are functions) so self[key] gets assigned to the function on line 10. Because of closure, the values of sfn and fn are saved when this is done.

The result of this is that, when Y’s version of test() is called, this._super gets set to the saved value of sfn, which is X’s version of test(), and then fn (which is Y’s version of test()) gets called via the apply() method. That is, whenever a method in the derived class is called, the corresponding base class method (if any) is available through this._super(). This means, of course, that the function referred to by this._super() varies, depending on which function calls it.

In my experience over years of ‘normal’ OOP using Java and C#, I have used a super() method only a handful of times, so you probably won’t need to use it that much. However, this clever solution makes it available in JavaScript if you need it.

Advertisements
Post a comment or leave a trackback: Trackback URL.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: