Inheritance in Javascript
Javascript is a very dynamic language, and you can implement inheritance and code reuse in multiple ways. Lately, I’ve grown fond of what Douglas Crockford calls Parasitic Inheritance.
By using parasitic inheritance, you can avoid using the new operator all together. The new operator isn’t evil, but it’s problematic in that if you write a function and you expect it to be called with the new operator but someone forgets it, wild thing can happen, and it can be really hard to debug. Let’s take an example:
function greet() {
alert("Welcome, everyone!");
}
function Greeter(name) {
this.name = name;
}
Greeter.prototype.greet = function() {
alert("Welcome " + this.name);
}
function Person(name) {
this.name = name;
this.greet();
return this;
}
Person.prototype = new Greeter();
Person.prototype.toString = function() {
return "I am " + this.name;
}
// Example 1
var name = "Andy";
var person = new Person("Coffeescripter"); // alerts "Welcome Coffeescripter"
alert(name); // alerts "Andy"
// Example 2
var name = "Andy";
var person = Person("Coffeescripter"); // alerts "Welcome Andy"
alert(name); // alerts "Coffeescripter"
This way of writing a class and saying that Person inherits from Greeter is rather ugly, so check out John Resigs implementation for some eye candy: http://ejohn.org/blog/simple-javascript-inheritance/
If you’re not familiar with how this works in Javascript, this may blow your mind. But it’s perfectly valid code, it’s just that the this variable points to different places depending on whether you used the new operator or not. When the new operator is used, this points to the new instance that was created. When you’re not using the new operator, this points to the object that the function was executed in, which in this case was the window object, and that is why this.name = name is overriding the value of the global variable name. This because var name = "Andy"; here is technically the same as window.name = "Andy";.
You can’t tell from inside the function if the new operator was used or not, so you can’t throw an exception if the new operator was omitted. You could just say that “all functions that begin with a capital letter should be called with the new operator, or else you’re stupid” and be done with it. And while that might be true, you’re still going to be the one debugging for an hour or so when the new guy who doesn’t know Javascript that well forgets about the new operator.
Parasitic Inheritance
So how about that parasitic inheritance then? It works like this instead:
var Greeter = function(name) {
return {
greet: function() {
alert("Welcome " + name);
}
};
};
function Person(name) {
var obj = Greeter(name);
obj.greet();
obj.toString = function() {
return "I am " + name;
}
return obj;
}
// Example 1
var name = "Andy";
var person = new Person("Coffeescripter"); // alerts "Welcome Coffeescripter"
alert(name); // alerts "Andy"
// Example 2
var name = "Andy";
var person = Person("Coffeescripter"); // alerts "Welcome Coffeescripter"
alert(name); // alerts "Andy"
The behaviour is the same if you call Person with the new operator or not. And because of that, it simpler just to forget about the new operator. To me, this code is more straight forward and less magical than the first example, which is always a good thing. One issue people is having with this approach is that for every Person you create, you also create new versions of the greet and toString functions. With the first example, all Person objects share the exact same greet function. That of course means less memory usage, but it also introduces a somewhat scary thought. If all Person objects share the same greet function, what happens when someone decide to replace a persons greet function, it doesn’t just affect that object, it affects all person objects.
This trouble with parasitic inheritance
Something I kept running into with parasitic inheritance was how I didn’t have access to the created object in the beginning of the function, like this:
function Person() {
var bazooka = Bazooka(this);
return {
shoot: function() {
bazooka.shoot();
}
};
}
var Bazooka = function(parent) {
return {
shoot: function() {
}
}
}
Disregarding that we have a cyclic reference here between Person and Bazooka, the problem is that this in var bazooka = Bazooka(this); isn’t pointing to the person object that we are creating, but the object that the Person function is running in; the window object. So how do we get around this? We could do something like:
function Person() {
var obj = {
shoot: function() {
bazooka.shoot();
}
};
var bazooka = Bazooka(obj);
return obj;
}
But that’s about as pretty and clear as VB’s let’s-use-a-variable-with-the-same-name-as-the-function-as-the-return-mechanism-because-function-names-never-change-and-nobody-will-forget-to-update-all-references-to-the-return-variable-when-the-function-is-renamed. So yeah, we can do better. Something like this:
function Person() {
var bazooka;
return {
_init: function() {
bazooka = Bazooka(this);
return this;
},
shoot: function() {
bazooka.shoot();
}
}._init();
}
It looks a lot better, and if you’re used to classical inheritance, you’re probably used to looking for property declarations at the top, a constructor right after that, and methods down below. If you’re the suspicious type, you might have noticed that by doing this, we are exposing the _init function to be called again. Which is true, there is nothing stopping you from doing: var p = Person(); p._init(); But if this means the end of the world for you, you can always add this._init = undefined; as the first line in the _init function, which makes it impossible to call it twice.
Privileged functions
In the first example, there is no way to have a private function that all your public functions can call, not even with John Resigs prettier version. But with parasitic inheritance, it’s very natural to hide variables and functions through closures, like this:
function Person(name) {
var bazooka;
var greet = function() {
alert("Hi! I'm " + name);
};
return {
_init: function() {
bazooka = Bazooka(this);
if(name == "Andy") {
greet();
}
return this;
},
shoot: function() {
if(name != "Andy") {
bazooka.shoot(name);
}
}
}._init();
}
Through the closure that the function call to Person creates, both shoot and _init has access to greet, bazooka and name.
Conclusion
The key difference between classical and parasitic inheritance is that the classical way is much more focused on an objects type. Change one object, and all other objects of that same type get’s changes. Parasitic inheritance doesn’t care about types. It’s more about assembling functions and properties to create a logical unit. Instead of caring if an argument to your function is of the type Person, you only care about whether or not it has the function greet. There is no right or wrong here, and the two can happily co-exists, but the thing that makes me use parasitic inheritance is how it’s aligned with the dynamic nature of Javascript.
About this entry
You’re currently reading "Inheritance in Javascript", an entry on The Coffeescripter
- Published:
- 2010.07.31 at 10.36
- Comments:
- 3 Comments
- Category:
- Javascript, Object Oriented
3 Comments
Jump to comment form | comments rss [?] | trackback uri [?]