1

I'm new to JS and I understand functions are the way to declare "class like" structures. I'm trying to do something like this:

function Game() {
  // Class stuff

  this.handleClick = function(e) {
    alert(e);
  }

  // Bind event to method previously defined
  $('#board').bind('click', function(e) {
    this.handleClick(e);              // <--- THIS IS THE PROBLEMATIC LINE
  });
}

Now, if in the "problematic line" I write:

  handleClick(e)

I get Uncaught ReferenceError: handleClick is not defined.

Instead, if I write:

  this.handleClick(e);

I get Uncaught TypeError: Object #<HTMLCanvasElement> has no method 'handleClick'

But then, if I do:

function Game() {
  // Class stuff

  this.handleClick = function(e) {
    alert(e);
  }

  var game = this;                    // <--- I ASSIGN this TO board
  // Bind event to method previously defined
  $('#board').bind('click', function(e) {
    game.handleClick(e);              // <--- THIS WORKS!! WHY????
  });
}

It works!?!?!

My questions are:

  1. Why does this happen this way? I know this can be problematic, but why assigning it to a variable changes it like that?
  2. Am I doing this wrong? Is there a better way to achieve something like this in a more elegant way?
leo
  • 1,103
  • 1
  • 8
  • 18
  • 1
    `this` depends on **how** you call a function. In your case `this` is the DOM element. – elclanrs Jun 26 '13 at 03:29
  • Ok, but why when I assign this to a variable it changes its value? – leo Jun 26 '13 at 03:30
  • Because it loses its special meaning and simply stores whatever `this` was at the time of assignment. – Qantas 94 Heavy Jun 26 '13 at 03:31
  • 1
    `this` outside the event handler is a different `this`. By caching it you're maintaining the outer value. When you introduce a new scope, your `this` will change according to _how_ the function is called. In your case, it's called like `fn.call(element, event, ...args)` where `element` is `this`. – elclanrs Jun 26 '13 at 03:31

1 Answers1

4

You found the one hole in Javascript's implementation of closures. (If you don't know what closures are, don't worry about it.)

this in Javascript references the object on which the function was called. So if you have an object with a property that happens to be assigned the value of a function, and you call that property, the this pointer references the object. It does not reference whatever this was where the function was created, nor does it reference the function itself.

var object = {};
object.myfunc = function() { console.log(this); } //<-- this will refer to object
object.myfunc();

In the case of an event handler, the handler function (in pure Javascript, ignoring jQuery) gets assigned to the DOM element, so it gets called from that element and hence this points to the DOM element.

Using a local variable, like game in your example above, means that the function you created (a "closure") will "close" around that variable, capturing the values of any variable in scope at the place the function was defined. Any variable except this. So your work-around is a very common way to handle this.

Another common way to handle this would be to use a library like Underscore.js which has a function called bind that does some higher-order-function magic to make sure this always points to what you want.

$('board').bind('click', _.bind(function(e) {
  this.handleClick(e);
}, this));

For this reason, Underscore.js is one of my favorite Javascript libraries (after jQuery), and I use it almost exclusively for the bind function.

webjprgm
  • 3,441
  • 2
  • 15
  • 14