[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