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), 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, 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.
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, 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.