[thelist] Problems with prototypal inheritance in JavaScript

Andrew Clover and-evolt at doxdesk.com
Fri May 23 18:53:29 CDT 2008


Jay Turley wrote:

> /* ThingList object */
> function ThingList() {
>     this.__proto__.initialize(arguments);
>     //this.initialize(arguments);
> }
>
> ThingList.prototype = new MYNAMESPACE.util.Hash(); // Prototypal inheritance
>
> // public methods
> //PropertyList.prototype.initialize = function(arguments) {
>     // prototypal inheritance does not support calling
>     // the superclass constructor
>     // Had to reference the __proto__ in the constructor above. Why? Not sure.
> //};

[Note: I am assuming PropertyList is a typo/previous version and it is 
meant to be ThingList].

You're being bitten by a few different confusing aspects of JavaScript's 
gratuitously-different-from-every-other-language object model at once here.

Firstly, this.__proto__.initialize() is not doing what you might think 
it is. 'this', during the constructor call, is a new object whose 
__proto__ is ThingList.prototype. So 'this.__proto__.initialize()' is 
actually calling 'ThingList.prototype.initialize()'. Because you have 
currently commented that out, it will end up calling 
'Hash.prototype.initialize()'.

However, 'this' inside your call to 'this.__proto__.initialize()' will 
not be the same object as the 'this' in the constructor (the new object, 
empty except for its __proto__). When you call a method with 
'something.method()', the 'something' becomes 'this' inside that call. 
So for 'this.__proto__.initialize()', which ends up calling 
'Hash.prototype.initialize()', 'this' is actually equal to 
'this.__proto__' (ie.: 'ThingList.prototype').

'Hash.prototype.initialize()' then writes the properties to the 
'ThingList.prototype' object, *not* the new object! This means you'll 
actually get only one set _length/_items/_keys properties, shared 
between all instances of ThingList, and they'll reset to empty every 
time you create a new ThingList!

You can use Function.apply() to call a method with 'this' set to 
something other than the object you're calling the method on, so 
'initialize.apply(this, arguments)' is better*. However don't call it in 
the constructor where you are at the moment; a constructor should always 
call its *own* initialiser (and, in the kind of object model you're 
using here, it shouldn't ever do anything else). The 
'this.initialize(arguments)' line was right all along.

Instead, have the initialiser call its base class's initialiser as the 
first thing it does. You can obtain a base class method explicitly:

     Hash.prototype.initialize.apply(this, arguments);

You could alternatively try to get the superclass automatically using 
'ThingList.prototype.__proto__' if you really want, though it's more 
typing for not much benefit; you *can't* really do it by looking at 
'this' because you can't be sure how many inheritance levels down 'this' 
will be if ThingList has itself been subclassed.

(* - Side note: If you need to support IE5.0, which doesn't give you 
Function.apply(), then either take the long way round:

     this._tempmethod= Hash.prototype.initialize;
     this._tempmethod(arguments);

Or, detect the lack of Function.apply() and hack Function.prototype to 
add the missing method in a similar way.)

-- 
And Clover
mailto:and at doxdesk.com
http://www.doxdesk.com/



More information about the thelist mailing list