Tuesday, March 12, 2013

Navigating the Mysterious World of Javascript Inheritance

If you've used Javascript for any amount of time, you've probably run across the term "prototypal inheritance". Maybe you've scratched your head and moved on because any library you're using neatly tucks the details of this feature of Javascript away and makes things just work. However, perhaps your now trying to figure out how those libraries work or want to extend them. This is where understanding the finer points of the language will enable you to figure out what all that code is actually trying to accomplish. In this post, I will attempt to summarize the basic concept of inheritance in Javascript and how can be be leveraged to build reusable components. The code samples are fairly simple and I encourage you to copy them into a page and try out different permutation yourself to visualize the mechanics of the language. My main goal is to determine the absolute essential pieces required to define objects and how instances of those objects behave. I'll start by examining a simple base object and its instances. Next I'll build on that concept to construct a parent/child definition and related instance objects. Finally, I'll explore how to add several convenience functions to manage creating object definitions and their related inheritance chains. I'll compare this against how existing libraries today implement these concepts to provide similar capabilities.

The Two Sides of Function



Before diving in, we need to know what our building blocks are and what they mean and can do for us. First, everything is an object in Javascript (this page has a nice summary of the concepts). As such, a Function is also an object with special powers. What gets confusing is that a function can serve two purposes - to do some processing like a traditional function would do or to define an instance of an object that contains certain properties and more functions. To illustrate, lets define a simple function, sum() and see how it can be used in either context:


var sum = function(x, y) { return x + y };

// Outputs 6
console.log(sum(2,4));

// Outputs {Object}
console.log(new sum(2, 4));


In the first case, we use it as a normal function to add 2 and 4 together to get 6. In the second case, we use the new keyword to create an instance of an Object. So what's the difference? The second usage is calling sum() as a constructor to create a new object based on the prototype property associated with the function. What's a prototype property? Just an object that is used to create the new object when you use the "new" keyword to call a function. By default, this object will contain one key - "constructor" which is the sum() function. When you use new, you'll get an object with "constructor" as the properties in the new object. You know this because you can call it directly just like sum():



// Outputs 6
console.log(sum.prototype.constructor(2,4));

// This is true
console.log(sum.prototype.constructor === sum);

// Outputs 6 too
console.log((new sum(2, 4)).constructor(2,4));


What's in a Prototype



Alright, so a basically empty object that simply has the original function reference in it is not all that useful. Let's add some more to our object:



// Empty constructor
var Calc = function () {};

// Add 2 properties and a function to the prototype object
Calc.prototype.x = 2;
Calc.prototype.y = 4;
Calc.prototype.sum = function () {
return this.x + this.y;
};

Now, two different instances:
var c1 = new Calc();
var c2 = new Calc();

// Outputs 6
console.log(c1.sum());
// Outputs 6
console.log(c2.sum());

c1.x = 5;
// Outputs 9
console.log(c1.sum());
// Outputs 6
console.log(c2.sum());


c2.x = 7;
// Outputs 9
console.log(c1.sum());
// Outputs 11
console.log(c2.sum());

// Both false - different objects like expected
console.log(c1 === Calc.prototype);
console.log(c2 === Calc.prototype);

// However,
Calc.prototype.x = 10;
Calc.prototype.y = 12;

// Outputs 17
console.log(c1.sum());
// Outputs 19
console.log(c2.sum());


That last part where I changed the prototype after creating the instances c1 and c2 caused "y" to change in both but not "x". But a few lines up we confirmed that neither c1 nor c2 where the same as Calc.prototype so how did that happen - aren't they completely different objects? Well, yes and no, this is the part where you can cause yourself hours of debugging trying to find something that you don't expect to happen (this page has some good material on the subject). This feature of prototypes is meant to save memory by not completely copying all the object properties from the prototype to the instance. When you access a property, you'll get the current version in the prototype unless you overwrite it in the instance. So, in this example, "x" was set to 5 and 7 so the change to the prototype had no effect. However, "y" was never touched so the change will be seen by both instances. If either c1 or c2 had set "y" prior to that change, the local value would override the prototype. This is the basic inheritance pattern in Javascript. Each instance knows what properties it "owns" and what prototype it inherited from. If it can't find it locally, it will search the prototype. In the example above, we have created a prototype in Calc where the constructor(), sum(), x, and y are defined. This is what is called the prototype chain and enables us to organize our code into reusable objects by inheriting common parts through the prototype. At this point, we have only created one level - the instance object (c1 and c2) inherited from the object definition (Calc). The next step is to create another layer by building another object definition based on a parent/base object.

Creating the Chain



Inheriting a base object's properties and methods is remarkably simple. It is the same pattern as creating an instance from the object definition. The only difference is, instead of defining a variable to receive the instance, we'll assign it to the prototype of the child object:



// Create the constructor function
var A = function () {
console.log('A.constructor');
}

// Add a function. Our new child object will
// inherit this functionality automatically
A.prototype.foo = function () {
console.log('A.foo');
}

// Now create the subclass constructor
var B = function () {
console.log('B.constructor');
}

// Inherit all the parents features
// This is the magic part
B.prototype = new A;

// Add a function unique to the child
B.prototype.bar = function () {
console.log('B.bar');
}



Now B has both a foo and a bar function. The B.prototype = new A line is the bare essentials required to establish the prototype chain. However, there is a small issue using the above convention. new A will cause A's constructor function to execute (just like it does for any new instance). Meaning, if you look at your console, the following message would appear:


> A.constructor


At this point, we haven't even created an instance of A or B that we intend to use. We've just setting up the inheritance between objects. So how do we prevent this from happening? Well, we could say that no constructor can do anything and then the above code would work fine. However, that is rather inflexible. Another approach is to add an intermediary object between the parent and child objects. This object will have an empty constructor. This will work around that problem and allow anything to be in the parent constructor and it won't run when setting up the inheritance:


// Instead of B.prototype = new A,
// use this method.

// Create an empty intermediate function
var I = function () { };

// Assign the parent's prototype to the intermediate's prototype.
// "I" will now inherit from "A".
I.prototype = A.prototype;

// Now create the new "I" which is "A" but without the constructor
// function that contains code that needs to execute.
B.prototype = new I;


Now, the A constructor will not run when creating the chain. Now that we have our inheritance setup, let's try to create and instance and use it:


b = new B();
b.foo();


This will cause the following output in the console:

> B.constructor
> A.foo


So the next questions is - what happened to A's constructor? Only the constructor for B ran and then the inherited function foo() ran as expected. The issue here is anything defined in B with the same name will override A causing not to run. The same behavior would occur if we also added a foo() function to B:



B.prototype.foo = function () {
console.log('B.foo');
}

b = new B();
b.foo();


Now the console will show only the B implementation of constructor and foo:


> B.constructor
> B.foo


This is the same behavior exhibited earlier when we would read from the property and it would return the value from the prototype but once we wrote to it, it was set in the instance and was no longer connected to the prototype. So the same pattern occurred here: a new instance of B will first check if there is an instance variable set, then the B prototype, and then the A prototype. Since the constructor for B is found first, it is run and that's it. The same thing happens when you add a foo() function to B - it overrides the foo() implementation in A. It is possible to cause the overridden functions from A to execute, however, it must be called explicitly:



B = function () {
// Run parent constructor first
A.prototype.constructor.apply(this, arguments);
console.log('B.constructor');
}

B.prototype.foo = function () {
console.log('B.foo');

// Run parent last
A.prototype.foo.apply(this, arguments);
}

b = new B();
b.foo();


Now, the console will output the following:


> A.constructor
> B.constructor
> B.foo
> A.foo


At this point, this all you need to start creating your own library of reusable objects. However, you might be wondering why libraries like Backbone, Prototype, and jQuery UI have these various extend methods that are suppose to make this all easier. Well, they do wrap up the above logic into a convenient function. Additionally, they all, in some fashion, add some extra functionality into the mix either to make it easier to develop or to ensure anything you build with them is consistent (and works) with the rest of the library.

The Missing Link



So far, we've accessed the parent functionality via the prototype on the parent's definition directly. This not the only way to get there:


B.prototype.foo = function () {
console.log('B.foo');

// Run parent last
A.prototype.foo.apply(this, arguments);

// These will also do the same thing:
//B.prototype.constructor.prototype.foo.apply(this, arguments);
//this.constructor.prototype.foo.apply(this, arguments);
//arguments.callee.prototype.foo.apply(this, arguments);

}


Notice we can eliminate the reference to the object definition completely (A or B) and just work with the current scope (this or arguments.callee) which keeps the code somewhat more generic and decoupled. However, we can shorten up the reference a little by setting a class property to the parent's prototype while defining the inheritance:



...
var I = function () { };
I.prototype = A.prototype;
B.prototype = new I;

// Replaces the "prototype.constructor"
// required above
B.__super__ = A.prototype;

B.prototype.foo = function () {
console.log('B.foo');
B.__super__.foo.apply(this, arguments);
}


Now its a little more explicit what were trying to do here. However, this still requires "B" in the call to foo() since __super__ is defined on the object definition not the instance. You could modify the constructor so that each time an instance is created it sets up a reference to the parent's prototype:



B = function () {
this.__super__ = this.constructor.prototype;
this.__super__.constructor();
console.log('B.constructor');
}

B.prototype.foo = function () {
console.log('B.foo');

// Run parent constructor last
// Don't need to use apply() if we know the
// arguments to send. foo() will run in the
// scope of "this"
this.__super__.foo();
}


Now we can just use this.__super__ to access anything in the parent prototype we might need. In general, we're going to do this when we override a parent's function (constructor(), foo(), etc) and would like to reuse the parent's implementation of the function. This is probably the most common reason to want this access, so wouldn't it be nice if we could just call this.__super__() inside the overridden function and the parents version of the same function would execute? Well, this is possible, but not without digging into our bag of Javascript tools and using anonymous functions and closures to build the illusion that the __super__() function actually exists:



// Define B.foo()
B.prototype.foo = function () {
console.log('B.foo');

// We're counting on that fact that something will
// wrap this function so the parent's version of
// foo() will be called when __super__() is called
this.__super__();
}

// Now, proxy the new function inside a closure that
// will become the actual foo function. This function
// will setup the reference to the parent's version of
// the function so its available inside B.foo()
B.prototype.foo = (function (_base, _call) {

var __super__ = function () { _base.apply(this, arguments); };

return function () {

var ___super___ = this.__super__,
_ret;

this.__super__ = __super__;

_ret = _call.apply(this, arguments);

this.__super__ = ___super___;

return _ret;
}

})(A.prototype.foo, B.prototype.foo);

var b = new B();
b.foo();


This example might seem a little over done for what it does, but its a simplified version of what jQuery UI is actually doing when you extend from the widget base object (or any other existing widget using the $.widget() function):



// Inherit from the base Spinner widget
$.widget( "ui.myspinner", $.ui.spinner, {

...
// Override _create ...
_create: function() {

this.options.min = this.options.min || 0;
this.options.max = this.options.max || 100;

if ( this.options.alignment != 'vertical' &&
this.options.alignment != 'horizontal' ) {
this.options.alignment = 'vertical';
}

// Call the base Spinner's version of _create here:
this._super();

this._value( this.element.val() );
this.uiSlider.slider( 'value', this.value() | 0 );
},
...


One of the side-effects of doing this is that there will be an undefined instance variable __super__ in "b". If you inspect it in Firebug, you'll see it there. Of course, the same thing happens in jQuery UI:


var s = $('#slider').slider().data('ui-slider');


If you create the slider on an element and then grab a reference to the actual slider instance object and inspect it in Firebug, you'll see the _super and _superApply properties lingering as undefined on the object. You also have the added layer of complexity because several anonymous functions are making it harder to debug issues in your code. I do like the convenience and simplicity of the notation, however, its not part of the language nor is it necessary for inheritance to work.

Putting It All Together



Now that we know what is and is not built into the base Javascript inheritance model, we can create some helper utilities to extend objects and add any special features we might want to have available. Here's a simple extend function which will take a parent object, a child constructor, and a prototype object for the child and return the child with the prototype chain setup and the desired prototype object attached. As part of the processing, it will proxy function collisions to add a _super() function so the parent's implementation can be optionally invoke inside the child's implementation:



function extend(parent, child, props) {

var subr = function () { };

// Setup the prototype chain
subr.prototype = parent.prototype;
child.prototype = new subr;

// Proxy collisions so _super() is available
// to conveniently invoke parent's function.
for ( var p in props ) {

if ( parent.prototype[p] ) {

// There is a collision - wrap up the methods in a proxy to
// add chaining via super()
if ( typeof(props[p]) == 'function' ) {

// Break the shared scope and lock in the current method
// by wrapping in another function to create a new parent scope
child.prototype[p] = (function (_base, _call) {

var _super = function () { _base.apply(this, arguments); }

var _proxy = function () {

var _ret, __ret,
__super = this._super

this._super = _super;
var _ret = _call.apply(this, arguments);

this._super = __super;

return _ret;
}

return _proxy;

})(parent.prototype[p], props[p]);

} else {

// Non-function collisions - child wins...
child.prototype[p] = props[p];

}

} else {

// No collisions detected. Copy it all across.
child.prototype[p] = props[p];

}
}

return child;
}


Its not a perfect version since it does not handle setting up the _super() function on the constructors. I wanted to keep the code simple here. The goal was to consolidate everything together to see how it fit together. Refer to the end of the post for a link to a more robust version and other options. Using the above function looks something like this:



// Create the constructor function
var A = function () {
console.log('A.constructor');
}

// Give it a function
A.prototype.foo = function () {
console.log('A.foo');
}

// Create the child constructor
B = function () {
console.log('B.constructor');
}

// Now extend A with B adding the following
// to the prototype. Any function collisions will be
// proxied so the _super() function is available to
// call the parent's implementation.

B = extend(A, B, {
bar: function () {
console.log('B.bar');
},
foo: function () {
console.log('B.foo');

this._super();
}
});

// Try it out ...
var b = new B();
b.foo();
b.bar();



Alright, so you want to have some of these capabilities available in your own library, isn't there something already available or do I need to figure out how to make my own? Yes, there are several options depending on what you need. Prototype has a Class object that you can use to setup object definitions and wrap up various helpers including a $super argument to child functions. If you're not interested in all the extra library goodies Prototype comes with, you can check out Base by Dean Edwards which focuses only on creating a consistent interface for defining and extending object classes. Finally, I created a simple base class implementation which pulls ideas from jQuery UI, Prototype, and other libraries I've encountered to establish consistency and useful tools for defining objects in Javascript.

In the end, whether you build your own or utilize another library, understanding the capabilities of Javascript inheritance will undoubtedly help you when writing your next web app.