Search code examples
javascriptprototypeprototype-chain

Add a method to prototype


I created a Sprite Class and then derived a Dragon class from the sprite class. I am trying to add in an action method only to the Dragon Class, but it is not working. I'm adding the action method to Dragon.prototype in the same way that I added methods to the Sprite class with Sprite.prototype, but if I create an instance of my Dragon class, I'm not able to call the action method I defined in the Dragon prototype.

If I change Dragon.prototype.action to Sprite.prototype.action, the code works perfectly. For this example, it wouldn't matter, but I want to be able to set up methods for specific classes later on. What am I doing wrong? Should I just settle for more method names? If the answer should be obvious I apologize, and thanks for your help.

var Sprite = function(spriteSheet, rows, columns, x, y, width, height){
    this.frameSpeed = 1;
    this.spriteSheet = spriteSheet;
//The starting Location
    this.x = x;
    this.y = y;
//The width and height of the sprite
    this.width = width;
    this.height = height;
//Variables for movement
    this.xSpeed = 0;
    this.ySpeed = 0;
//rows and columns of our spritesheet
    this.rows = rows;
    this.columns = columns;
//our variables needed to animate the sprite.
    this.sourceWidth = this.spriteSheet.width/this.columns;
    this.sourceHeight = this.spriteSheet.height/this.rows;
    this.sourceX = 0;
    this.sourceY = 0;
    this.frameCount = this.rows * this.columns;
    this.currentFrame = 0;
}

Sprite.prototype.update = function(){

    this.y+=this.ySpeed;
    this.x+= this.xSpeed;

    if(this.currentFrame >= this.frameCount){
        this.currentFrame = 0;
    }

    this.row = Math.floor( this.currentFrame / this.columns);
    this.column = Math.floor( this.currentFrame % this.columns);

    this.currentFrame+=this.frameSpeed;
    this.sourceX = this.column * this.sourceWidth;
    this.sourceY = this.row * this.sourceHeight;

}

Sprite.prototype.draw = function(){
     context.drawImage( this.spriteSheet, this.sourceX, this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height);
}

var mySprite = new Sprite(smiley,4,4,100,50,100,100);

var Dragon = function( x, y, width, height){
    var dragon = new Sprite(dragonSheet,4,3,x,y, width,height);
    dragon.frameSpeed = .5;
    dragon.xSpeed = 4;
    return dragon;
} 

Dragon.prototype.action = function(){
    if(this.x>500){
        this.x = -100;
    }
}

var dragon1 = new Dragon(70,170,80,50);

Solution

  • In contrast to your Sprite constructor, the Dragon function does return an object - and is therefore no more a constructor, but a mere factory function. It does create and return a Sprite instace, which will inherit from Sprite.prototype, not from Dragon.prototype. See What is the 'new' keyword in JavaScript? for details.

    To fix this, use Dragon as a constructor. this will be the instance, and to initialise it you'd .call() the Sprite function on it before giving it dragon-specific properties. The newly created instances would then inherit from Dragon.prototype.

    To set up the prototype chain correctly, you need to make Dragon.prototype inherit from Sprite.prototype - so that all created Dragon instances will have the Sprite methods as well, like here.

    function Dragon(x, y, width, height) {
        Sprite.call(this, dragonSheet, 4, 3, x, y, width, height);
        this.frameSpeed = .5;
        this.xSpeed = 4;
    }
    Dragon.prototype = Object.create(Sprite.prototype);
    Dragon.prototype.constructor = Dragon;
    
    Dragon.prototype.action = …;
    
    var dragon1 = new Dragon(70,170,80,50);