Prototype inheritance in JavaScript II
This post has been updated at 09/07/2011.
This article is an continuation of the Prototype Inheritance in JavaScript where I explained what is prototype and how the basics of prototype inheritance in JavaScript works. Today I'll go on with the new keyword and how to use the more advanced OOP techniques as super() etc.
This article is an continuation of the Prototype Inheritance in JavaScript where I explained what is prototype and how the basics of prototype inheritance in JavaScript works. Today I'll go on with the new keyword and how to use the more advanced OOP techniques as super() etc.
Many people are confused by OOP in JavaScript, namely by inheritance. There isn't any standard way how inherit classes in CommonJS standard or anywhere else, so each framework use its own solution.
There are many solutions which are trying to implement classical inheritance (i. JS.Class, Class implementation in MooTools framework, Base.js and much more). I personally don't like it, but I understand why people do this kind of stuff.
Inheritance in JavaScript
The new Keyword
Here comes a very important “aha” moment – new just creates a new object and assign object.__proto__ to constructor.prototype. Easy-peasy, right?
var Model = new Function;
Model.prototype.database = {};
var task = new Model;
task.__proto__;
// { database: {} }
So for inheriting instance methods you just needs to assign inheritedClass.prototype to superClass.prototype:
var sys = require("sys");
var Model = new Function;
var Post = new Function;
Post.prototype = Model.prototype;
Model.prototype.save = function () {
sys.puts("Saving ...")
};
var post = new Post;
post.save(); // => "Saving ..."
This will work, however when we'll add a new method into Post.prototype, it'll be added into Model.prototype as well since both points to the same object. So let's fix that:
var Model = new Function;
var Post = new Function;
Post.prototype = new Model;
Model.prototype.save = function () {
sys.puts("Saving ...")
};
var post = new Post;
post.save(); // => "Saving ..."
This works as well, but what if we initialize some instance-related data in the constructor, i. e. this.key? We don't want to propagate this mess into our prototype. Not mention if the constuctor takes some init data. So let's fix that as well:
var Model = function () {
this.data = [];
};
var Post = new Function;
var temp = new Function;
temp.prototype = Model.prototype;
Post.prototype = new temp();
Model.prototype.save = function () {
sys.puts("Saving ...")
};
var post = new Post;
post.save(); // => "Saving ..."
sys.puts(sys.inspect(Post.prototype)) // {}
That's finally better, but it's quite a lot of type because of such a simple thing as inheritance. Fortunatelly Node.js provides sys.inherits which is doing pretty much the same as we are doing now, so let's refactor our code:
var Model = new Function;
var Post = new Function;
sys.inherits(Post, Model);
Model.prototype.save = function () {
sys.puts("Saving ...")
return true;
};
var post = new Post;
post.save(); // => "Saving ..."
Cool. Let's take a look how to extend methods in subclasses. According to John Resig it should be something like:
var Model = new Function;
var Post = new Function;
Model.prototype.save = function(type){
sys.puts("Saving ...");
return true;
}
Post.prototype = new Model();
Post.prototype.save = function () {
sys.puts("In the Post.prototype");
return Object.getPrototypeOf(this).save();
};
var post = new Post();
post.save()
// In the Post.prototype
// In the Post.prototype
// Saving ...
This works, but as you can see the function is called twice, because for the first time Object.getPrototypeOf(this) returns Post.prototype. Fortunately the solution is easy:
Post.prototype.save = function () {
sys.puts("In the Post.prototype");
var prototype = Object.getPrototypeOf;
return prototype(prototype(this)).save();
};
// In the Post.prototype
// Saving ...
Or there is another way how to do the same in Node.js:
var Model = new Function;
var Post = new Function;
Model.prototype.save = function(type){
sys.puts("Saving ...");
return true;
}
sys.inherits(Post, Model);
Post.prototype.save = function () {
sys.puts("In the Post.prototype");
return this.constructor.super_.prototype.save();
};
var post = new Post();
post.save()
Also, if you want to use some instance variables, you need to do this.constructor.super_.prototype.save.call(this) which will evaluate the method in this which is the current instance.
Inheriting Static Methods
This is the tricky part, because static methods are just properties of the constructor, so they aren't in its prototype. Which means you have to do it manually, but I think you shouldn't need it anyway.
blog comments powered by Disqus