Advanced material - "this" and callbacks

Introduction

There are a number of subtleties with JavaScript this which you need to understand to work effectively with objects, and which make its use less straightforward than the use of this in other languages such as Java and C++. These subtleties are particularly important when it comes to callbacks.

"this" and the window object

Firstly, what does this refer to if it is not being used in the context of an object? For example, if the function below was run, what would be displayed?

function thisTest()
{
    alert(this);
}
On Firefox it displays:
[object Window]
The output demonstrates what this refers to when out of the context of an object. It refers to a "default object" which in a web browser is the window object, representing the browser window as a whole. So the example below would work too:
function thisTest()
{
    window.alert("alert is actually a method of the window object!");
    this.alert("... and here, *this* refers to the window so this works too!");
}
Both alerts are displayed. alert() is actually a method of the window object, but because the window object is the environment's "default" object, we can normally leave window out; it's included here just to illustrate the point. And because this also refers to the window object in this context, the second alert will be displayed too.

"this" and callbacks - the problem

The problem most commonly encountered with "this" is when it is being used in a callback function. As we have seen, a callback function is a function that does not run immediately, but at a later time, when some event has occurred (such as the user clicking a button, or the response from an AJAX request is received). So imagine we had a Cat and we wanted it to meow if we pressed a button, e.g:

// cat.js
class Cat 
{

    constructor(n,a)
    {
        this.theName=n;
        this.age=a;
    }

    meow()
    {
        alert(`${this.theName} says meow!`);
    }
}



// Create the cat
const tom = new Cat("Tom", 7);

// When the user clicks the button, the meow method will run
document.getElementById("meowbtn").addEventListener("click", tom.meow);

HTML:
<html>
<head>
<script type='text/javascript' src='cat.js'></script>
</head>
<body>
<input type='button' id='meowbtn' value='Meow!' />
</body>
</html>

If you try this out, and click the button, does the expected output:

Tom says Meow!
occur? No. Instead, you will get something like:
undefined says Meow!
Why is this happening?

Dealing with "this" issues in callbacks with bind()

Luckily, the issue is easy to deal with. You can simply use the bind() method. bind() does what it says on the tin: it binds the callback function to the specified object, so that when the callback is called, this will refer to the correct object. So to rewrite our Cat example so they work correctly:

class Cat 
{

    constructor(n,a)
    {
        this.theName=n;
        this.age=a;
    }

    meow()
    {
        alert(`${this.theName} says meow!`);
    }
}


// Runs when the page loads

// Create the cat
const tom = new Cat("Tom", 7);

// When the user clicks the button, the meow method will run
    document.getElementById("meowbtn").addEventListener("click", tom.meow.bind(tom));

Note how the callback tom.meow() is now bound to the object tom. This means that this will refer to the correct object, tom, when tom.meow() is called as a callback on response to a button click, or indeed any other event.