0

I'm new to JavaScript and I'm trying to write a simple game-loop. Here's my Game object:

var Game = {
    timers: [],
    start: timestamp(),
    stopTimers: function() {
        consoleEntry('All timers have been stopped.');
        this.timers.length = 0;
    },
    update: function(elasped) {
        for (var i = 0; i < this.timers.length; ++i) {
            var timer = elapsed - timers[i].startTime;
            console.log('Timer: ' + timer + '. Timer interval: ' + this.timers[i].interval + '.');
            if (this.timers[i].interval <= timer) {
                this.timers[i].times--;
                this.timers[i].report = true;
                if (this.timers[i].times != 0) {
                    this.timers[i].startTime = elapsed;
                }
            }
        }
    },
    render: function() {
        for (var i = 0; i < this.timers.length; ++i) {
            if (this.timers[i].report) {
                consoleEntry('Timer: ' + this.timers[i].name + ' (' + this.timers[i].times + ' remaining).');
                this.timers[i].report = false;
                if (this.timers[i].times == 0) this.timers.splice(i, 1);
            }
        }
    },
    gameLoop: function() {
        var elapsed = timestamp() - this.start;
        this.update(elapsed);
        this.render();
        requestAnimationFrame(this.gameLoop);
    },
    startGame: function() {
        console.log(this);
        requestAnimationFrame(this.gameLoop);
    }
}

I call Game.startGame(); when the <body> loads. Here's the error I receive: Uncaught TypeError: undefined is not a function. It refers to this.update(elapsed); inside the gameLoop function. For some reason I don't understand, when I do console.log(this) inside startGame I get the object it belongs to (which is awesome), but when I do the same inside gameLoop, I get the Window object.

Does anyone know why this happens?

Thank you!

user1563544
  • 379
  • 3
  • 17
  • Are you calling new Game, and then calling startGame on the new object you've just made? Or are you literally just calling Game.startGame()? – Jazzepi Jan 31 '15 at 19:06
  • 1
    literally :) I shouldn't, eh? – user1563544 Jan 31 '15 at 19:09
  • 1
    You have to bind `this` to the function you're passing to `requestAnimationFrame()` - `requestAnimationFrame(this.gameLoop.bind(this));` – Pointy Jan 31 '15 at 19:09
  • @Jazzepi the `Game` variable refers to a plain object, not a constructor function. – Pointy Jan 31 '15 at 19:10
  • I just don't understand why `this` returns `Window` in `gameLoop` – user1563544 Jan 31 '15 at 19:11
  • @Pointy Woops. Looks like you're right. Jumped the gun on expected patterns ;)~ – Jazzepi Jan 31 '15 at 19:13
  • Binding most certainly will help - you have to do it in both calls to `requestAminationFrame`. JavaScript functions don't have implicit relationships to objects, and the value of `this` in a function completely depends on how the function is invoked. – Pointy Jan 31 '15 at 19:20
  • Interesting. And it worked! I need to read more about this. Feel free to submit an answer, I'll mark it :) – user1563544 Jan 31 '15 at 19:22

1 Answers1

0

The easiest way to see what "this" refers to is to look at which object comes before the function in the calling statement's dot-notation.

For example, if you say

document.createElement(..)

In the function's definition, "this" will refer to document.

All functions have such an object. Global functions refer to window.

window.console === console; // true

So, when you execute "requestAnimationFrame", "this" refers to the window.

You can specify what "this" refers to by using the Function prototype functions "call" and "apply".

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

I'm not particularly familiar with window.requestAnimationFrame, but for your purposes I might suggest one of two options:

Maybe simply using "call" could work

requestAnimationFrame.call(this, this.gameLoop);

Or, if not, a closure will do the trick.

requestAnimationFrame(
   (function(g){ 
       return function() { g.gameLoop(); }; 
    })(this)
);

And, for more information about "this" in Javascript:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

Hope this helps!

DRAB
  • 445
  • 2
  • 10
  • Any function, global or otherwise, that's invoked without an explicit receiver gets `window` as the value of `this` in non-strict mode; in strict mode it gets `undefined`. – Pointy Jan 31 '15 at 19:26