Prototype Inheritance in JavaScript
This post has been updated at 09/07/2011.
Old-school way how to handle inheritance in JavaScript is so-called prototype inheritance. A lot of people don't very understand how it actually works. This technique works only with objects, there isn't anything like classes.
Old-school way how to handle inheritance in JavaScript is so-called prototype inheritance. A lot of people don't very understand how it actually works. This technique works only with objects, there isn't anything like classes.
Let's take a look at simple example:
var language = {type: "Programming Language"};
var javascript = {version: "1.9"};
// Let's set prototype of javascript object
javascript.__proto__ = language;
javascript.version; // "1.9"
javascript.type; // "Programming Language"
The important part is obviously javascript.__proto__ = language. It sets prototype of the javascript object to language, which means that if given property doesn't exist in the javascript object, interpreter tries to find it in the language object.
Since JavaScript 1.9 there is also alternative syntax for this via Object.create:
var language = {type: "Programming Language"};
var javascript = Object.create(language, {version: {value: "1.9"}});
Inheritance works in a chain, so you can inherit from object which inherits form another object which inherits from yet another object etc. Like this:
javascript.__proto__.__proto__.inheritanceChain = function () {
sys.p(this);
};
javascript.inheritanceChain();
// { version: '1.9' }
language.inheritanceChain();
// { type: 'Programming Language' }
Pretty useless example, right? Let's take a look at something more complex (more readable version is available as a Gist):
var assert = require("assert");
var model = {
// Run all validations for current object
// and throw assertion error if some of
// the assertion methods don't return true.
validate: function () {
for (key in this.validations) {
var validation = this.validations[key];
assert.ok(validation.call(this));
};
},
// Validations getter, it simply returns validations
// or throw error if the validations aren't set.
get validations () {
if (this._validations) {
return this._validations;
} else {
throw new Error("You have to set validations!");
};
},
// Validations setter.
set validations (object) {
this._validations = object;
},
};
// Create a new object.
var task = {title: "Learn Prototype Inheritance"};
// Here the magic comes, we set prototype of task
// to model, so if given property doesn't exist in
// task, it will try to find it in prototype.
task.__proto__ = model;
// Let's define some validations.
task.validations = {
title: function () {
return !! this.title;
}
}
// We can access properties of task object ...
sys.puts(task.title);
// ... as well as properties of task prototype
task.validate();
I think that the example is pretty self-explanatory. You have a model object which will be prototype of all models. In fact in real-world application we'd probably have a model which would be a prototype of abstract task object and this object would be a prototype of each single tasks. Easy-peasy.
But ... what about static methods? And constructors? The new keyword? Calling superclass method? We'll take a look next time. Inheritance in JavaScript is quite a complex topic.
PS: I'm talking about JavaScript in general, because I'm writing some JavaScript using Node.js, if you are going to use this technique in browser, you should know that Internet Explorer doesn't support the __proto__ property (what a surprise, right?).
PS 2: JavaScript 1.8.1 introduced getPrototypeOf method which is basically the same as __proto__, it just have a slighly different syntax: Object.getPrototypeOf(object) John Resig wrote quite descriptive post about this method as well.
