technology from back to front

Subclassing in JavaScript, part 1

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 call f.apply(x, [y,z]) and f(y,z) will be called with “this” bound to x.

* 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"; and x.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)“, where F is a function object, does (roughly) the following:

* creates a new blank object (call it “res“)
* res.\_\_proto\_\_ = F.prototype
* F.apply(res, [2, 3])
* return res

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

  1. this need is felt in any object-oriented language whether prototype-based or otherwise
  2. Self, for one, does provide a good solution to this
  3. JavaScript doesn’t.

In Self, you can create a subclass by cloning an example object of the sort you want to extend, extending it, and using it as a new example object. However, in JavaScript objects aren’t created by cloning; it uses constructors.

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.

Read Part 2

by
Paul Crowley
on
24/07/06
  1. 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
    on 12/09/06 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= [];</p>

    <p>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. 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
    on 27/04/07 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. You write, “I don’t know how to make JavaScript behave like a sensible prototype-based language”. Douglas Crockford writes:

    function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
    }

    Then, all you need to do is call object(foo) to create an object whose prototype is foo.

  6. 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. Actually, this is very much like Self’s clone method. Maybe an example use would clarify:

    var parent = {a: 3};
    var child = object(parent);
    alert(child.a); // displays “3″
    child.a = 4;
    alert(child.a); // displays “4″
    delete(child.a);
    alert(child.a); // displays “3″
    parent.a = 7;
    alert(child.a); // displays “7″

  8. 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. 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 http://dotnetcaffe.blogspot.co.uk/search/label/JavaScript under JavaScript category. Fell free to criticize the code in any way you want…just don’t flame :).

 
 


− five = 4

2000-14 LShift Ltd, 1st Floor, Hoxton Point, 6 Rufus Street, London, N1 6PE, UK+44 (0)20 7729 7060   Contact us