Search code examples
javascripteventsconstructor2d-games

javascript constructor property becomes undefined after event is called


var Context = {
    canvas: null,
    context: null,
    create: function(canvas_tag_id, size){
        this.canvas = document.getElementById(canvas_tag_id);
        this.canvas.width = size[0];
        this.canvas.height = size[1];
        this.context = this.canvas.getContext('2d');
        return this.context;
    },
    fps:1/30
};

$(function(){

// Initialize
Context.create('canvas', [798, 652]);

var s_size = [Context.canvas.width, Context.canvas.height]; // screen size

function Player(){
    this.rect = [0, s_size[1]-40, 20, 40];
    this.color = 'blue';

    this.create = function(){
        // function for creating player object

        Context.context.beginPath();
        Context.context.fillStyle = this.color;
        Context.context.rect(
            this.rect[0], this.rect[1], this.rect[2], this.rect[3]);
        Context.context.fill();
    };

    this.control = function(){
        // player movement control function

        if (event.which == 39 || event.keyCode == 39){
            alert(this.rect);
        }
    };

    this.update = function(){
        this.rect[0] += 1;
    }
}

// player instance creation

var archie = new Player();

// game loop functions

function events(){
    // Keydown events

    function keydown(){
        window.addEventListener('keydown', archie.control);
    }

    keydown();
}

function update(){
    archie.update();
}

function render(){
    Context.context.clearRect(0, 0, canvas.width, canvas.height);

    archie.create();
}

function game(){
    events();
    update();
    render();
}

setInterval(game, Context.fps);
});

As you can see the problem isn't the organization of the code but the event handler, because the player class's update method is working just fine even though it's created after the event handler. what exactly is the problem here and how do i solve it?


Solution

  • Inside the event handler, this is always the element the event handler was bound to, not the constructor for the function passed in.

    To write your code a lot shorter, you're doing

    var Player = function() {
    
        this.rect = "something";
    
        this.control = function(){
            if (event.which == 39 || event.keyCode == 39){
                alert(this.rect); // NOPE ... this is the element
            }
        };
    }
    
    var archie = new Player(); // create instance
    
    window.addEventListener('keydown', archie.control); // some function in the instance
    

    If you just have to have the object as the this value, use bind

    window.addEventListener('keydown', archie.control.bind(archie));
    

    Also note that your event handler callback is missing the event argument, and relying on the global event, which is not supported in all browsers (Firefox), so you should be doing

    this.control = function(event) {...