The Idea Garden is made up of a number of suggestions that each follow a certain structure. The person who wants to use the Idea Garden in a new environment has to write templates and a bit of boilerplate code. As part of the Idea Garden meets Gidget project, I’ve been doing some work porting the Idea Garden architecture from a Firefox plugin, CoScripter.
The purpose of this post is to discuss how I used a particular style of Javascript prototype inheritance that I discovered on Stack Overflow. In some respects, it’s trying to communicate to new developers from the perspective of a new Javascript developer and explain some gotchas on the way. This isn’t meant to be a really in-depth guide to Javascript prototypes or the Javascript object model – there’s some good reading on the Internet already that discusses the intricacies of the language.
The Prototype-based Programming Model
Javascript’s object-oriented model is prototype-based rather than class-based: what this means, practically, is that unlike most classical object-oriented languages (Java, Smalltalk, C++, Python, Ruby, etc.), there is no distinction between a class definition and an object. In other words, as soon as you “declare” a “class” in Javascript, it exists in the world immediately!
This has led to some oddities in Javascript, such as there being at least two ways to declare a new class, with each of these ways looking a little different from each other. In some respects, trying to think in terms of normal object-oriented programming in Javascript only gets you mired in some unnatural programming practices.
Because Javascript isn’t classicly object-oriented, trying to comprehend its inheritance model is a bit tricky. For this reason, along with reasons outlined in the Gang of Four book), and others, I’d actually recommend composition over inheritance.
But, in this particular case, I was asked to implement an architecture to use inheritance because we have a number of Idea Garden suggestions, and Idea Garden suggestions behave similarly and borrow a number of common functions, but might have some small specialized differences in their content.
Which Inheritance Style, Exactly?
There is no shortage of Javascript inheritance styles. Because there’s really no accepted “way” of doing it (or of NOT doing it), anyone who wants to do it has to really work at studying what they want, exactly.
In the end, I emulated a style following this Stack Overflow post by Sean McMillan because it (1) used the Javascript Module pattern, which was pervasive throughout this code and (2) used Object.create
, which basically does the inheritance for me (Object.create
is ECMAScript 5 and from what I understand, basically replaces new
). However, the answer, while providing a pretty good code example, didn’t explain all of the concepts behind it that I needed to know.
The Return of the Prototype
As I mentioned above, Javascript is a protoype-based language, and as such doesn’t have class definitions. The module pattern probably comes the closest to emulating a “class definition”, but is still not exactly a class definition. If I create a module:
IdeaGarden = (function() {
var exports = {},
environmentName = "Gidget";
exports.hostInfoToIdeagarden = function() {
// Do stuff here
};
return exports;
}());
it exists as soon as the code runs, and I can modify the object’s state, use its functions, and so forth, without doing an Object.create
or new
call.
An important part of inheritance is to separate the definition from the instantiation. In general, you want to inherit from a certain state of class, rather than whatever you happened to have used and extended most recently.
To do this, we define a prototype in our module that we will inherit from when we do an Object.create
.
Vehicle = {};
Vehicle.AbstractVehicle = (function() {
var exports = {};
exports.prototype = {};
exports.prototype.init = function() {
this.identifier = "Abstract";
this.passengers = 0;
this.speed = 0;
this.running = false;
};
exports.prototype.getName = function() {
return this.identifier;
};
exports.prototype.accelerate = function() {
if (this.running) {
this.speed = this.speed + 10 - this.passengers;
}
};
exports.create = function(identifier) {
var ret = Object.create(exports.prototype);
ret.init();
return ret;
};
return exports;
}());
This code, which pretty much conceptually matches to Sean’s code in his post, illustrates a few important points.
First, we have a callable “exports.create”, which creates a prototype of the base class, runs its “init” function, and returns the prototype for us to use. The fact that it returns a prototype is important! Unlike in the normal module pattern, where you would declare additional private members somewhere in the body of Vehicle, in the inherited class from the prototype, only members declared using this
in the init
function will be visible to instantiated classes and their descendants.
The difference between “what belongs to the module” and “what belongs to the new created class” isn’t exactly difficult, but it tripped me up as a beginner because of the close association between the module class having its own private members and the members appearing in the init
function.
Because the create
function returns a prototype, everything needs to exist in the scope of the prototype, rather than in the module!
The corrilary to this is that public members need to exist in the init
function and that public methods need to be part of the prototype, rather than the module. Here’s an example method:
exports.prototype.accelerate = function() {
if (this.running) {
this.speed = this.speed + 10 - this.passengers;
}
};
Inheriting from the Base class
Once you realise the importance of returning the prototype, the rest of it pretty much falls into place. Since you’re using Object.create
to create a new version of the prototype, you can create multiple versions of the base class and modify them freely without worrying about changing the definitions(that’s something that you would have to worry about otherwise). Here’s some code to inherit from the base class.
Vehicle.Car = (function() {
var exports = {};
exports.prototype = Object.create(Vehicle.AbstractVehicle.prototype);
exports.prototype.init = function(identifier) {
Vehicle.AbstractVehicle.prototype.init.apply(this, [identifier]);
this.identifier = identifier;
this.passengers = 4;
};
exports.prototype.cruiseControl = function(newSpeed) {
while (this.speed < newSpeed) {
this.speed += 1;
}
};
exports.prototype.startVehicle = function() {
console.log("Starting the I4");
this.running = true;
};
exports.create = function(identifier) {
var ret = Object.create(exports.prototype);
ret.init(identifier);
return ret;
};
return exports;
}());
Let’s go through some of this code as well.
First, you’ll notice that exports.prototype
in this version does an Object.create(Vehicle.AbstractVehicle.prototype)
. This basically matches the prototype to the base class. In the exports.prototype.init
function, we call the init
from the base class as well – it’s essentially equivalent to our “super” call from other languages. Note that we pass in our arguments (identifier
in this case) in an array – that’s just how apply works. Our variables from the base class will be initialized at that time, along with any additional variables we want here.
As mentioned above, because we return the prototype, every method that we want to extend the base class with needs to be preceeded with prototype
. That’s why we have exports.prototype.cruiseControl
and exports.prototype.startVehicle
. Note that we can use any of the variables and methods we declared in the base class within these methods as long as we precede them with this
.
Finally, we have our create function, which is NOT in the prototype space because we want to be able to call it from our code. This is pretty much boiler-plate code from the base class but we need it.
Now, we can use this just as we might expect (with a little help from JQuery assigning an identifier to outputDOM):
var workCar = Vehicle.Car.create("Work");
workCar.startVehicle(); // Outputs "Starting the I4 of Work" to the console.
outputDOM.append(workCar.getName() + ". Speed: " + workCar.speed + "
"); // Outputs "Work. Speed: 0".
workCar.accelerate();
outputDOM.append(workCar.getName() + ". Speed: " + workCar.speed + "
"); // Outputs "Work. Speed: 6"
If we create another car using Vehicle.Car.create
it’ll hold its own set of variables.
In Closing
I find Javascript is a little bit of an adventure because it’s a language that appears to have evolved to do things that no one envisioned it to do fifteen years ago. It used to be written directly in HTML as part of “onclick” attributes, and now it’s manipulating the DOM, applying CSS elements, and animating on-screen. Everything’s evolved heavily in the back-end as well. Douglas Crockford attempted to apply a classical inheritance model to Javascript but then gave up, eventually saying that “it was a mistake”. However, a lot of this information is still on the web, and it’s difficult to identify what the state-of-the-practice is for Javascript these days.
I do want to, however, keep up to date and be sure that if I’m writing Javascript now, it’ll be recognizable to people who are reading my code two, three, five, ten years from now. I like trying to keep code reasonably simple and clean – especially in a situation like the one I’m in where others are going to be working off of the code that I started.
If anyone notices that I’ve made a mistake in my understanding, or would like to recommend other ways to accomplish what I’ve outlined here, I’d be glad to hear about it and update this post accordingly – I’m not a Javascript expery by any means, but by writing and self-reflecting on what I’ve done, I would like to keep improving my skills and my understanding. So, if you have any suggestions or improvements please feel free to drop me a line or leave a comment.
Like this:
Like Loading...