Subclassing in JavaScript, part 1
July 24th, 2006 Paul Crowley
What’s the right way to create a subclass in JavaScript?
Wrong question, say the JavaScript advocates. JavaScript isn’t one of those fuddy-duddy old class-based languages. It’s something much more exciting: a prototype-based language! So remember, when you work with JavaScript, remember never to refer to “classes”, because JavaScript doesn’t have them, and it only shows you’re stuck in the old way of thinking.
I’m sure that these sentiments have done enormous harm to the reputations of real prototype-based languages, so let me banish it right here. JavaScript is not a prototype based language; it most closely resembles a class-based language, but all its mechanisms for doing the work of a class-based language are horribly broken, which is why its advocates try to pretend it’s something else.
The most famous real prototype-based language is Self. In Self, you create new objects by cloning existing objects; this clones both methods and instance variables (”slots”). If you want to be able to add methods to an object after creation, you can create a special object (a “traits object”) and set it as a parent object of some other object; this child object will delegate to a parent object to find a value for a slot that it doesn’t have its own value for.
JavaScript works rather differently. Here’s the detail on how it works:
Functions in JavaScript can refer to a special magic variable “
this” which is effectively an implicit parameter. If “f” is a function, you can callf.apply(x, [y,z])andf(y,z)will be called with “this” bound tox.Objects are dictionaries. Those dictionaries can map string keys to any Javascript value, including functions: you can validly do either or both of
x.name = "foo";andx.action = function () {};“
x.action();” is magic; it’s not the same as “tmp = x.action; tmp()” as it would be in a language like Python. Instead, it means “x.action.apply(x)“.If you look up a key in an object which is not set in the object, and the object has the “
__proto__” key set, then it will delegate to the “__proto__” object to try to find the key. (In IE, this key effectively has a non-string name that can’t be programatically accessed, but the effect is the same). That object may delegate recursively to its “__proto__” in turn.Functions are objects have dictionaries associated with them, so you can do
var F = function() {}; F.foo = "bar"; print(F.foo);and get back “bar” as you might expect.“
new F(2, 3)“, whereFis a function object, does (roughly) the following:- creates a new blank object (call it “
res“) res.__proto__ = F.prototypeF.apply(res, [2, 3])return res
- creates a new blank object (call it “
And that’s it. Well, roughly - see the ECMAScript standard for the gritty details.
From this you can immediately see that it’s not a prototype-based language; objects are not created by cloning a prototype, and indeed JavaScript doesn’t even come with a convenient way of cloning objects out of the box. What JavaScript calls prototypes are more like Self’s “traits objects”, which take the place of classes - they hold what is shared between objects of the same class. Objects are created with a constructor which also specifies the “traits object”, just like in a class-based language.
Not convinced? Wait until you see the discussion of inheritance.
Any object oriented language provides some way or other of saying “I want these objects to be like those objects, except…”. In Python, when writing a class description you directly mark it as a subclass of another class, and those methods that are not overwritten are inherited. In Self, you clone one of those objects to act as your new prototype, and modify it as you see fit, possibly by adding a traits object so that you can add methods applicable to the new object.
So here’s the question I came in with in JavaScript: how do I do it there? If you try to discuss this question with a JavaScript advocate, they’ll try to avoid the question, by tripping you up when you use words like “subclass” and “inheritance” to describe what you’re trying to do.
The truth of the matter is that
- this need is felt in any object-oriented language whether prototype-based or otherwise
- Self, for one, does provide a good solution to this
- JavaScript doesn’t.
The usual way you’ll see inheritance done in JavaScript is as follows:
function Child () {
... child constructor goes here
}
Child.prototype = new Parent();
a = new Child();
so “a” is now a Child object, and Child is a subclass of Parent. (Since JavaScript isn’t a prototype-based language I make no apologies for using class-based terminology here.) This approach is badly flawed, as this example demonstrates:
function Parent () {
this.array = [];
}
a = new Parent();
b = new Parent();
a.array.push("a");
b.array.push("b");
print(a.array); // prints "a"
print(b.array); // prints "b"
function Child () {
this.somethingelse = "somethingelse";
}
Child.prototype = new Parent();
aa = new Child();
bb = new Child();
aa.array.push("aa");
bb.array.push("bb");
print(aa.array); // prints "aa,bb"
print(bb.array); // prints "aa,bb"
What’s happened here? Every time the “Parent” constructor is called, it creates a new array and puts it in the “array” slot on the new object, so every “Parent” object has its own array. But this constructor is not called for the “Child” objects. Instead, the “Child” objects share a single instance of the “Parent” object for their __proto__, which includes a single instance of the “array” object. aa.array and bb.array refer to the same array, and so changing one changes the other.
Now sometimes you want arrays (and other referenced objects) to be distinct between instances, and sometimes you want it to be shared between instances, but you never want it to be one thing in the superclass and another in the subclass. I have to confess at this point that I don’t know how Self addresses this problem, but I know that whatever solution it has will work consistently for derived behaviour as well as direct because the same mechanism is used for object creation.
I don’t know how to make JavaScript behave like a sensible prototype-based language; maybe there isn’t a way. But there are ways to make it behave like a sensible class-based language. After reading about a dozen different solutions to this online, I came to one which I think goes to the heart of the problem in the simplest way I can see, and which preserves the most flexibility. It’s reasonably efficient, too. This has become long enough, so I’ll describe that in a future blog entry.
Entry Filed under: Technology, Programming, Rant
9 Comments Add your own
1. matthew | July 24th, 2006 at 11:23 pm
The last bit you describe about the children sharing the same parent instance reminds me of multiple virtual inheritance in C++. Except there you had to work to get such confusing behaviour - it seems so much easier in Javascript…!
2. Jeff Watkins | September 12th, 2006 at 9:00 pm
Sorry, your security code “feature” wiped out the long response, multiple examples, and explanations. I’ll try to simply post a corrected version of your last example:
function Parent()
{
}
Parent.prototype.sharedArray= [];
function Child()
{
this.instanceMember= 5;
}
Child.prototype= new Parent;
Child.prototype.constructor= Child;
This is the correct way to implement a shared property in JavaScript.
The thing to keep in mind is that the object initialisers run for each new object created. Therefore, if you don’t want a per-instance property, you need to define it on the prototype rather than in the initialiser.
3. pdl_e | April 13th, 2007 at 1:39 pm
There is another approach to implement JavaScript inheritance - a “lazy” inheritance which has all benefits of prototype-based inheritance like typed classes, but also eliminates necessity to declare external scripts in proper order and automatically resolves and loads (if necessary) dependencies to external scripts that contains related classes.
You can find more about it on Alternate inheritance
4. fantomx11 | April 27th, 2007 at 8:43 pm
Here is my rewritten Child that doesn’t have the problem you describe and doesn’t require any helper functions to implement.
function Child() {
Parent.prototype.constructor.apply(this);
this.somethingelse = "somethingelse";
}
5. Bill T | August 29th, 2007 at 12:17 am
You write, “I don’t know how to make JavaScript behave like a sensible prototype-based language”. Douglas Crockford writes:
Then, all you need to do is call
object(foo)to create an object whose prototype isfoo.6. Paul Crowley | August 29th, 2007 at 8:36 am
Bill T: this is essentially the trick I discuss in Part 2. Although JavaScript calls this a “prototype” the name is misleading; it most closely resembles the vtable pointer of a class-based language.
7. Bill T | September 5th, 2007 at 1:19 am
Actually, this is very much like Self’s clone method. Maybe an example use would clarify:
8. Paul Crowley | September 5th, 2007 at 11:20 am
Bill T: I thought you had to use “traits objects” to get similar behaviour in Self, am I wrong? Could you give a sample of Self code that illustrates the same behaviour in Self resulting from the use of clone()? Thanks!
9. M | May 12th, 2008 at 10:50 am
Hi,
I have tried an different techniques related to JavaScript inheritance subject.
The code and the explanation are to long to post them here so if anyone is interested to take a look over it you can find it at www.dotnetcaffe.net under JavaScript category. Fell free to criticize the code in any way you want…just don’t flame :).
Leave a Comment
Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed