JavaScript as a prototypical language offers loads of solutions to implement classes and class inheritance. When coming from an Object Oriented language such as Java or Ruby, these solutions aren’t obvious at the first glance.
I started coding JavaScript years ago and always found a solution for my specific case. But things felt messy and I always thought “Is this really the right way of doing it?”. Well, after all those years, experience took away this feeling and now I want to share some stuff that finally meant a break-through of understanding for me.
There are two different concepts of doing inheritance in JavaScript. One is using the prototype
property of a function as a mean to create something similar to a “class”. The other concept is using the constructor function itself to create a class dynamically during runtime.
For both concepts I’ll show you their basic idea and compact helpers for creating inheritance in an elegant way.
Let’s start with the prototype
-based concept.
Prototype-based
In JavaScript everything is relative to a function, e.g. the scope of local variables, or the this
-keyword. A class itself is also created through a simple function which in turn becomes the constructor. Every function implicitly is an object and additional properities can be added. A function automatically has a specific property: prototype
which in turn is an object (or “hash”) itself. This property will be used as template when a new instance is created. Here’s an example:
var A = function(){}; // This is the constructor of "A" A.prototype.constructor = A; A.prototype.value = 1; A.prototype.test = function() { alert(this.value); } var a = new A(); // create an instance of A alert(a.value); // => 1 alert(a.test()); // => 1
The constructor of A
is an empty function. You can add initializers here of course. The prototype
contains a Number value and a function.
new A();
basically does all the magic. It creates a new “empty” object, copies the keys and theirs values of A.prototype
into this object, and passes the object into the constructor of A
accessible via the this
keyword.
Let B inherit from A:
var B = function(){}; B.prototype = new A; B.prototype.constructor = B; B.prototype.test = function() { return "B says " + A.prototype.test.call(this); }; var b = new B(); alert(b.test()); // => "B says 1"
Giving B
the prototype of a new instance of A
is the way to do inheritance in JavaScript. And A.prototype.method.call(this, arg1, arg2, ...);
is the way to call methods of a “base class” (kind of like super.method(arg1, arg2, ...);
in Java).
The advantages of using the prototype
approach is speed and a low memory usage. The disadvantage is that private variables and functions cannot be shared between public methods since there’s no way to create a common closure.
Closure-based
When using the closure-based approach of creating classes, the constructor of a class directly is the common closure for private variables and methods.
var C = function() { // a private function var print = function(msg) { alert(msg); }; this.value = 1; this.test = function() { print(this.value); // calls our private function }; }; var c = new C(); alert(c.value); // => 1 alert(c.test()); // => 1
Since we never assigned the prototype of C
, it is an empty object. The constructor of C
however “fills” each instance with values and methods by assigning them to the object itself via this.xxx = ...
.
Doing inheritance this way is possible but pretty ugly (especially if you want instanceof
to work correctly) unless you use my closure-based helper below 😉
Helper: prototype-based
The implicit call of a constructor when assigning a new instance of A
as the prototype of B
and the long winded code for calling “super” are very error prone. This makes you wish having a helper for cleaning things up. Using a helper you can do the following:
var X = PClass.create({ val: 1, init: function(cv) { this.cv = cv; }, test: function() { return [this.val, this.cv].join(", "); } }); var Y = X.extend({ val: 2, init: function(cv, cv2) { this._super(cv); this.cv2 = cv2; }, test: function() { return [this._super(), this.cv2].join(", "); } }); var x = new X(123); var y = new Y(234, 567);
init
is the constructor of a PClass
(which stands for “Prototype-Class”). The parameters you pass to a new Xxx(...);
call will be passed on to this init
method.
Also note the calls to this._super(...);
– this will call the base class implementation of the same method, which is much more intuitive than using ...prototype.method.call(this, ...)
.
The helper required to do this kind of inheritance is inspired by John Resig’s solution. Here’s my version of it:
(function(){ var isFn = function(fn) { return typeof fn == "function"; }; PClass = function(){}; PClass.create = function(proto) { var k = function(magic) { // call init only if there's no magic cookie if (magic != isFn && isFn(this.init)) this.init.apply(this, arguments); }; k.prototype = new this(isFn); // use our private method as magic cookie for (key in proto) (function(fn, sfn){ // create a closure k.prototype[key] = !isFn(fn) || !isFn(sfn) ? fn : // add _super method function() { this._super = sfn; return fn.apply(this, arguments); }; })(proto[key], k.prototype[key]); k.prototype.constructor = k; k.extend = this.extend || this.create; return k; }; })();
The trick is to add a “magic cookie” to the constructor when creating the prototype for a new class. I’m using the private method isFn
for that which cannot be accessed outside the closure – which in turn means that no matter what you pass in you can never mess with the mechanism 😉
Helper: closure-based
Sometimes speed and memory are not that important for an application. But maintenance and usability are. In this case a closure-based way of doing inheritance can make sense. Having a means of private methods sometimes just “feels” better…
My helper allows you to implement inheritance in the following way (CClass stands for Closure-Class):
var Person = CClass.create( function(firstName, lastName) { // a private method var threeTimes = function(s) { return [s, s, s].join(", "); } // the public methods/fields return { // a public field val: 123, // a public method name: function() { return firstName + " " + lastName; }, greet: function() { // call our private method and a class method return threeTimes("Hello " + this.name()); } }; }); var Rusitschka = Person.extend( function(firstName) { // call the constructor of our base class this._super(firstName, "Rusitschka"); return { // overwriting a field from super class val: 321, // overwriting the name method of Person name: function() { // call the base class method in our context (this) return "[[[" + this._super() + "]]]"; } }; }); var p = new Person("Peter", "Schmidt"); var r = new Rusitschka("Steffen"); alert(p.name()); // => "Peter Schmidt" alert(r.name()); // => "[[[Steffen Rusitschka]]]"
Just like the prototype approach you can use this._super(...);
to call the base class implementation of a function. The hash object which the constructor returns automatically becomes the public interface of the class/instance.
And here’s the helper that does the trick:
(function(){ CClass = function(){}; CClass.create = function(constructor) { var k = this; c = function() { this._super = k; var pubs = constructor.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; }; })();
Summary
The two helpers PClass
and CClass
can help alot to implement readable and maintainable inheritance in JavaScript.
It is your choice which way of inheritance to chose for your specific problem. Speed+low memory => PClass; privacy => CClass.
And keep in mind: it is not possible to mix both concepts inside one class hierarchy.
Happy coding 🙂