views:

105

answers:

2

I'm writing a simple platform game using javascript and html5. I'm using javascript in an OO manner. To get inheritance working i'm using the following;

// http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/
function copyPrototype(descendant, parent) {
    var sConstructor = parent.toString();
    var aMatch = sConstructor.match(/\s*function (.*)\(/);
    if (aMatch != null) { descendant.prototype[aMatch[1]] = parent; }
    for (var m in parent.prototype) {
        descendant.prototype[m] = parent.prototype[m];
    }
};

For the sake of this post consider the following example;

function A() {
 this.Name = 'Class A'
}
A.prototype.PrintName = function () {
 alert(this.Name);
}

function B() {
 this.A();
}
copyPrototype(B, A);

function C() {
 this.B();
}
copyPrototype(C, B);

var instC = new C();

if (instC instanceof A)
  alert ('horray!');

As i understand it i would expect to see a horray alert box, because C is an instance of C & B & A, thanks to polymorphism. Am i wrong? Or am i just using the wrong method to check? Or has copyPrototype nackered the instanceof operator?

Thanks as always for taking the time to read this!

Shaw.

+5  A: 

The problem is that the copyPrototype function only copies the properties from a constructors prototype to another one, for example, at the end, the intenal [[Prototype]] link of C.prototype simply points to Object.prototype.

The prototype chain of instC and constructor's prototypes look like this:

                [[Prototype]]
    A.prototype -------------->|-------------------|
                               |                   |
    B.prototype -------------->|  Object.prototype | ---> null
                               |                   |
    C.prototype -------------->|-------------------|
        ^
        |
      instC

The instanceof operator traverses the prototype chain, your instC object, as you can see, will have on its prototype chain only C.prototype and Object.prototype.

You can achieve what you want by setting your constructor's prototypes to be object instances of their "parent" constructors, for example:

function A() {
  this.Name = 'Class A'
}

A.prototype.PrintName = function () {
  alert(this.Name);
}

function B() {
  //..
}
B.prototype = new A();
B.prototype.constructor = B; // fix constructor property


function C() {
  //..
}

C.prototype = new B();
C.prototype.constructor = C; // fix constructor property

var instC = new C();
if (instC instanceof A)
  alert('horray!');

Now the prototype chain of the instC object looks like this:

           ---------------        ---------------        ---------------
 instC --> | C.prototype | -----> | B.prototype | -----> | A.prototype |
           ---------------        ---------------        ---------------
                                                                |
                                                                V
                                                       --------------------
                                                       | Object.prototype |
                                                       --------------------
                                                                |
                                                                V
                                                               null

Recommended article:

CMS
@CMS, that form of inheritance is ridiculously easy to understand -- way more than Crockford's [`begetObject()`](http://javascript.crockford.com/prototypal.html) technique. Is there any downside to your method, or any advantage to `begetObject()`? Is `this` still what you expect it to be? And do you use this technique yourself?
Andrew
yes that's a nice solution. I've read so many different approaches to OO in javascript. I'll give this go! Cheers.
Shawson
hmm- the trouble with this method, which I should have really pointed out in my example, is that all of my constructors actually accept a parameter (position) which needs to get passed back down through the chain. Any ideas how that would be possible using this method?
Shawson
A: 

Ok I've found a solution which keeps the instanceof function working, as well as allowing me to pass constructor parameters through the inheritance chain. The solution is detailed here; https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Model - my class structure now looks like this;

function A(p) {
 this.Position = p || new Vector2d(0,0);
}

function B(p) {
 this.base = A;
 this.base(p);
}
B.prototype = new A;

function C(p) {
 this.base = B;
 this.base(p);
}
C.prototype = new B;

if(C instanceof A)
  alert (' it worked!! '); // you now see this alert box!

Thanks CMS for highlighting to me why this wasn't working!!

You can check out the full project (well an older build which, at time of writing has yet to see this new OO method put in the whole way through) up at http://8weekgame.shawson.co.uk/ - just check out my latest posts.

Shawson
Hi @shawson, an alternative instead of assigning `this.base` on each constructor, if that property is not useful for you, you can invoke the other constructor directly, setting the right `this` value and passing the `p` argument, you can do that by using the [`call`](https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Function/call) method, for example, inside `B`: `A.call(this, p);`
CMS