Pseudo-Classical Subclassing in JavaScript
I wanted to improve my understanding of subclassing so I decided to research the documentation and blog about it. In JavaScript, subclassing is a way for a class to inherit data, variables and functions, from another class. The class that is being inherited from is referred to as the superclass and the class that is inheriting is the subclass. How subclasses are made depends on the class pattern being used to define the superclass and subclass. Since I’ve struggled with subclassing in the pseudo-classical pattern, I will be focusing on that case.
The standard pseudo-classical class will have properties that are set in the constructor and functions that are associated with the constructor’s prototype. When I refer to a class’s prototype I mean the class constructor’s prototype. Any potential subclass needs access to both the properties and the functions of that class. Getting the superclass’s properties is straight forward. The superclass’s constructor can be reused with the call method. By passing the subclass’s ‘this’ binding to the call method, instances of the subclass will inherit all the properties of the superclass. Getting the superclass’s functions from its prototype isn’t as intuitive.
The first way that comes to my mind is to set the subclass’s prototype to the superclass’s prototype. What this does is make the subclass and superclass have the same prototype. Any new functions I add to the subclass would also be added to the superclass. Part of subclassing is that the subclass receives new properties and functions that are not in the superclass.
The correct way in JavaScript is to use Object.create. The subclass’s prototype is set equal to the result of calling Object.create with the superclass’s prototype being passed in as an argument. This creates a delegation relationship between the subclass’s prototype and the superclass’s prototype. Any calls to functions that do not exist on the subclass’s prototype will cause a look up to occur. The instance of the subclass will look to see if its superclass’s prototype has that function and if it does, run the function with its ‘this’ binding.
Object.create causes the relationship between the subclass’s prototype and constructor to be lost. To reestablish this connection, the subclass prototype’s constructor can be set equal to the subclass’s constructor. This completes subclassing for the pseudo-classical pattern, but I wanted to know more about how Object.create forms the delegation relationship between the subclass’s prototype and superclass’s prototype.
According to documentation on MDN, Object.create returns a new object. The prototype property of this new object is the prototype that was passed in to Object.create as an argument, so the superclass’s prototype. Prototypes themselves are objects, so the subclass’s prototype can be set equal to the object returned by Object.create. Since the subclass’s prototype is an object it will have a prototype property that is the superclass’s prototype. This forms a prototype chain which JavaScript uses to form the delegation relationship.
The prototype chain can be used to link multiple classes together. For example, given three classes A, B, and C, I want C’s prototype to have access to the functions on A’s and B’s prototypes. To accomplish this I can set up a prototype chain between A, B, and C. First I would use Object.create to set B’s prototype to a new object with A’s prototype as its prototype. Then I would use Object.create again to set C’s prototype to a new object with B’s prototype as its prototype. After setting up the prototype chain I would want to make sure I reset B and C prototypes’ constructors to B and C respectively. Now if I call a function with an instance of C that is on B’s prototype, but not C’s prototype, a look up will happen to B’s prototype. If a function I want to call is on A’s prototype and not C’s or B’s prototypes, then a look up will first happen to B’s prototype followed by a look up to A’s prototype.
There are a few important notes about subclassing in JavaScript that I thought I should mention. What I covered works for pseudo-classical subclassing in ES5 JavaScript. It would also work in ES6 JavaScript. However, ES6 JavaScript can do the same thing with the class, extends, and super key words. JavaScript does not support multiple-inheritance. The prototype chain can be as long as I want it to be, but it cannot contain classes that inherit from more than one direct superclass. In my example I couldn’t have C delegate to both A and B. C has to delegate to either B which delegates to A or A which delegates to B. A class can have multiple subclasses. For example, classes C, D, and E can all delegate to B.