In most programming languages, the “this” keyword is used in the non-static code of a class to refer to the current object.

For example:

class Car {
    constructor() {
        this.wheels = 4;
    }
    addWheels(n) {
        this.wheels += n;
    }
}

let car = new Car();
car.addWheels(2);
console.log(car.wheels);

In this example, while the constructor of the class ‘Car’ is running, the variable ‘this’ is set to the object being built.
In this kind of context, it is very clear to which object the variable ‘this’ refers to in the constructors’ code and class methods. But in real life, you will encounter references to this in other contexts where its use will be less obvious, and where you risk introducing bugs by modifying code.

In this article, we will see other contexts where this keyword can be used and its meaning.

BEFORE YOU BEGIN…

Please remember that Javascript is not a classic object-oriented language and that most of the time, it difficult to understand the ‘this’ key word. To begin with, when reading Javascript code you have to remember that there is only code and objects, all the other concepts are only syntactic sugar!

For example, we could rewrite the previous code like:

function Car() {
    this.wheels = 4;
}

function addWheels(n) {
    this.wheels += n;
}

let car = {};
Car.call(car);
addWheels.call(car, 2);
console.log(car.wheels);

This code does the same thing as the previous code. We see that the class Car is only a set of functions that manipulates a mysterious object in a variable this. A function which recalls on an object built from a class is only a reference to the ‘call’ method of the function (which at runtime is an object too). The first parameter passed to the call function is only the value assumed by the variable this during the execution of the method. The new keyword just calls the constructor function on an empty object.

GOOD REFLEXES TO HAVE: IN WHAT CONTEXT IS THE CURRENT CODE CALLED?

When trying to understand code that uses the this keyword, the good reflex is to look at the context where the code which uses this has been called.

IN A CALL / APPLY CALL

As shown in the previous example, a function is an object. We use the call and apply functions to directly set the value of this at runtime.
In the following example we will display the object passed as a parameter in the console.

function f()
{ 
    console.log(this);
} 
f.call({hello: 'world'});

IN CODE THAT IS NOT IN A FUNCTION

There are two cases to consider:

  • If you are in strict mode, this is worth undefined;
  • If we are not in strict mode, this is the global object in NodeJS or the window object in a browser. In the following example, the value displayed in the console will depend on the mode in which the code runs.
function f()
{
    console.log(this);
}
f();

Warning, this rule is also valid for a function defined inside another function!
In the following example, this is not the hello world object, but global, window or undefined depending on how this code is executed!

function f()
{ 
    function g() 
    { 
        console.log(this);
    }
    g();
}
f.call({hello: 'world'});

IN A CALL TO NEW OR TO THE NOTE ‘.’ TO ACCESS A GETTER / SETTER OR A METHOD

In this case, Javascript behaves as expected, and this has the value of the created object (for a new call) or the object before `.` (for other cases). In the following example, we will display a Car object in the console.

class Car {
    get f() {
        return this;
    }
}

let car = new Car();
console.log(car.f);

IN A FLECHEE FUNCTION

In this case, the value of this is static to the value of this when the function is created.

In the following example, on the first call, helloWorld will be displayed twice; during the second call, we will display the global object (because executed in NodeJS) twice; during the first isolated call, we will display the helloWorld object, lord of the second, we will display the global object.

let helloWorld = {hello: 'world'};
function f() {
    console.log('dans f, this vaut', this === global ? 'global' : this === helloWorld ? 'helloWorld' : '???');
    let p = () => console.log('dans p, this vaut', this === global ? 'global' : this === helloWorld ? 'helloWorld' : '???');
    p();
    return p;
}

console.log("premier appel");
q = f.apply(helloWorld);
console.log("appel isolé 1");
q();
console.log("deuxième appel");
q = f();
console.log("appel isolé 2");
q();
premier appel
dans f, this vaut helloWorld
dans p, this vaut helloWorld
appel isolé 1
dans p, this vaut helloWorld
deuxième appel
dans f, this vaut global
dans p, this vaut global
appel isolé 2
dans p, this vaut global

Let’s compare this example with the following example, where we will replace p with an undeflected function.

let helloWorld = {hello: 'world'};
function f() {
    console.log('dans f, this vaut', this === global ? 'global' : this === helloWorld ? 'helloWorld' : '???');
    function p() { console.log('dans p, this vaut', this === global ? 'global' : this === helloWorld ? 'helloWorld' : '???'); }
    p();
    return p;
}

console.log("premier appel");
q = f.apply(helloWorld); 
console.log("appel isolé 1");
q(); 
console.log("deuxième appel");
q = f(); 
console.log("appel isolé 2");
q();

In this case, the variable this in p will always be global, while the variable in f will have a different value in each call. In the first call, she will be worth helloWorld, in the others, global.

premier appel
dans f, this vaut helloWorld
dans p, this vaut global
appel isolé 1
dans p, this vaut global
deuxième appel
dans f, this vaut global
dans p, this vaut global
appel isolé 2
dans p, this vaut global

AFTER A BIND() CALL

The bind() method of a function is used to set the value of this for all calls to this function, regardless of the context.
In the following code, we create two functions b and c, c is bound to the someObject object while b is not.

let someObject = new String("object");
let otherObject = null;

b = function () {
    console.log("dans b, this vaut", this === someObject ? "someObject" : this === otherObject ? "otherObject" : '???');
}; 

c = function () {
    console.log("dans c, this vaut", this === someObject ? "someObject" : this === otherObject ? "otherObject" : '???');
}.bind(someObject);

b.apply(someObject);
c.apply(someObject);

otherObject = {
    b: b,
    c: c
};

otherObject.b();
otherObject.c();

The two functions are executed in two contexts: with apply and with the `.` notation to call a method of an object.
The output after executing this code is as follows.

dans b, this vaut someObject
dans c, this vaut someObject
dans b, this vaut otherObject
dans c, this vaut someObject

We see that for c, this is always worth someObject, regardless of the calling context. For b, the value depends on the context in which the function is called.

TO GO FURTHER…

This article is still too short to talk about all the details related to the use of this keyword to go further. I suggest you refer to the documentation mozilla of this keyword to get more details and more examples.
For ES6 enthusiasts, the mozilla documentation of arrow functions can also help to understand the implementation choices behind the operation of the this keyword in these functions.

I leave you three interesting articles on the subject: