7

Possible Duplicate:
set attribute with javascript super method

I am trying to create a simple game in HTML5 for fun. I have an Entity class that is supposed to be the superclass of the Player class.

function Entity(x, y) {

    this.x = x;
    this.y = y;

    this.tick = function() {
        //Do generic stuff
    }
}

function Player(x, y) {

    this.parent.constructor.call(this, x, y);

    this.tick = function() {
        //Do player-specific stuff
        this.parent.tick.call(this);
    }
}

Player.prototype = new Entity();
Player.prototype.constructor = Player;
Player.prototype.parent = Entity.prototype;

The problem is at this line:

this.parent.tick.call(this);

I get an error displayed in the JavaScript console of chrome: "Uncaught TypeError: Cannot call method 'call' of undefined".

I don't get it and I've spent a long time trying to find posts of a similar issue. My call to the superclass' constructor works fine but the call to the superclass' tick method does not work.

I'm very new to making games so I have no idea if this a good setup (calling superclass tick from subclass tick). If there is a better, more typical way that people use, please tell.

Thanks.

Community
  • 1
  • 1
Altherat
  • 701
  • 1
  • 13
  • 21
  • Why don't you have the generic stuff on the prototype? You could easily access it there. – Bergi Dec 07 '12 at 12:17
  • javascript has no classical inheritance like you are used from other classical languages like java. You should read into prototypal inheritance and learn about it's differences to the classical one. – Christoph Dec 07 '12 at 14:21

2 Answers2

7

Adapting this answer to your code:

function Entity(x, y) {

    this.x = x;
    this.y = y;

    this.tick = function() {
        //Do generic stuff
    }
}

function Player(x, y) {

    this.parent.constructor.call(this, x, y);

    var oldtick = this.tick;
    this.tick = function() {
        //Do player-specific stuff
        oldtick.call(this);
    }
}

Player.prototype = Object.create(Entity.prototype);
Player.prototype.constructor = Player;
Player.prototype.parent = Entity.prototype;
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I have a doubt. Why do we need to pass `this` in: `this.parent.constructor.call(this, x, y);`. Well I know, this would work like this. but I din't find any explanation for this. – Veer Shrivastav Feb 03 '15 at 11:44
  • @VeerShrivastav: You need to pass the instance because otherwise the constructor wouldn't know where to set up its properties. – Bergi Feb 03 '15 at 12:23
4

Your question inspired me to look around and I found what I think is a great article by Josh Gertzen about this concept.

I blatantly copy from his article some code to set up an extends method on classes:

function Class() { }
Class.prototype.construct = function() {};
Class.extend = function(def)
{
    var classDef = function()
    {
        if (arguments[0] !== Class)
        {
            this.construct.apply(this, arguments);
        }
    };
    var proto = new this(Class);
    var superClass = this.prototype;
    for (var n in def)
    {
        var item = def[n];                      
        if (item instanceof Function) item.$ = superClass;
        proto[n] = item;
    }
    classDef.prototype = proto;
    classDef.extend = this.extend;      
    return classDef;
};

After which your case is as simple as:

var Entity = Class.extend({
    tick: function()
    {
        alert('Entity tick');
    }
});

var Player = Entity.extend({
    tick: function()
    {
        alert('Player tick');
        arguments.callee.$.tick.call(this);
    }
});

p = new Player();
p.tick();

Which will alert Player tick and then Entity tick.

Mark
  • 18,730
  • 7
  • 107
  • 130
  • 10
    Looks like a overcomplication to me :-) – Bergi Dec 07 '12 at 12:35
  • +1 for effort, although Bergi answer is cleaner. – WTK Dec 07 '12 at 12:39
  • 2
    If this is the only superclass call in the application, it's an overcomplication of course. But I find that the second block of code is clearer than the other methods. And you only need to copy-paste the first block once and then forget about it; no more thinking about prototypes :-) – Mark Dec 07 '12 at 12:51
  • Yeah... It's an overcomplicated answer, but the link did remind me of the 'call' and 'apply' methods, which allow you to set the 'context' (this == 'context') of a function call, and that was useful enough for me. – Charlie Sep 06 '14 at 22:43