Help! This is not this!

Help! This is not this!

Here’s the story of me, this, and JavaScript. You may think about JavaScript what you may (I’m yet unsure if I like it or not[1]), but it is one of the most useful programming languages you can have in your toolbox. So, right now, I’m on my way to learn it. Which immediately made me run into a problem that might well affect nearly anyone who starts with JavaScript.

If JavaScript is not your first language[2], the concept of this in JavaScript may confuse you. In languages like C++ or Python, this (or self in Python) always points to the class instance.

Not so in JavaScript. Which bites you early on when you define your first handler, let’s say a click handler. And why would you start with JavaScript, if not to handle button clicks in a web page.

Here’s a simple example:

class Counter
{
  constructor()
  {
    this.counter = 0;

    document.getElementById("count").onclick = this.count;
  }

  count() {
    this.counter++;
    document.getElementById("counter").innerHTML = this.counter;
  }
}

window.addEventListener('load', function(event) {
  let c = new Counter();
});

So now you think that clicking the element labeled count (which will usually be a button in your HTML page) will increase the counter in the element labeled counter (which may well be a label in your HTML page).

But you are in for a big surprise. Clicking the button does nothing. At least nothing that’s visible. The label does not count up, it is still zero. When you run the debugger in the browser[3], you see that the variable counter is undefined. When you click the button, the value becomes NaN, because that’s what you get when you add one to undefined.

And here’s why: The this in the click handler is not the class Counter. Instead it’s the button that the handler was bound to with onclick. And this button does not have a counter field, so it’s undefined.

What now? You cannot get at the class instance in the handler, so defining an extra class variable which stores this won’t help, because you couldn’t access that too.

My first solution, which made me cringe a bit, but worked, was to introduce a global variable named classthis, set it to this in the constructor and use it whereever I want to access this. But we all frown upon global variables, don’t we? So what to do?

Fortunately, the makers of JavaScript / ECMAscript saw the dilemma, too. So, with ECMAscript 5, they gave us the bind() function. It allows you to set the this on a function. Using bind(), the class in the example above becomes:

class Counter
{
  constructor()
  {
    this.counter = 0;

    document.getElementById("count").onclick = this.count.bind(this);
  }

  count() {
    this.counter++;
    document.getElementById("counter").innerHTML = this.counter;
  }
}

And now it works. This points to the instance of the counter object and clicking and counting works like a charm.


1. Ok, I already like it. But will I ever love it?
2. Or, as in my case, not even your second, third, fourth or so language.
3. In Firefox, you get it by pressing CTRL-SHIFT-i simultaneously.

links

social