What is “this” in regular functions and arrow functions?

Hi, today I’m going to describe you how the keyword “this” work inside a regular function and an arrow function.

We have this button – the user can click this button and then a name will appear in the browser.

The HTML file will be this – index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Javascript THIS</title>

        <style type="text/css">
            button {
                color: white;
                text-decoration: bold;
                font-size: 1em;
                width: 200px;
                height: 50px;
                background-color: #0000b2;
                margin-top: 50px;
                border-radius: 5px;
            }
            </style>
    </head>
    <body>
        <button>Add name</button>
        <p>List of names:</p>
        <ul id="names"></ul>

        <script src="app.js"></script>
    </body>
</html>

The Javascript file will be this – app.js

I have 2 classes (ES6)NameField class to create a <li> element with a name and append this element to the <ul> element that is already created in the HTML file.

NameGenarator class – that have the eventListener that is “listen” for a user click and then executing a “addName” method.

// Class to create <li> element and append that element to the <ul>
class NameField {
    // Initialization
    constructor(name) {
        const field = document.createElement('li'); // Creating <li> element
        field.textContent = name; // Adding content
        const nameListHook = document.querySelector('#names'); // Selecting the <ul> element
        nameListHook.appendChild(field);
    }
}

// Class to generate name when user click the HTML button
class NameGenerator {
    // Initialization
    constructor() {
        const btn = document.querySelector('button'); // Selecting the button 
        btn.addEventListener('click', addName()); // Listen when user click the button 
    }

    // Method of the NameGenerator class 
    addName() {
        const name = new NameField('Cristina'); // Creating a new NameField object with 'Cristina' as name
    }
}

const gen = new NameGenerator();

Ok, take a look of the “addEventListener“:

btn.addEventListener('click', addName()); // Listen when user click the button 

if we call the addName() method like that, we will have this result in our browser:

addName is not defined : because it doesn’t find it and this is because the method addName() is attached to the class NameGenerator

And for use the methods that are attached to the class we need to tell Javascript that I want to access to that methods that are in that classes and to do that we need to use the “this” keyword

What does “this” do?

Well, “this” essentially refer to the object the code is in.

So in this case the code will be use the “this” keyword because “this” will give us access to the object (the class NameGenerator) and then we can call methods, properties of that object (the class NameGenerator)

btn.addEventListener('click', this.addName()); // Listen when user click the button

Result:

Now we can see the name, but is weird because we did’t click the button and the name is there….hmmm and also if I click the button nothing happen, no errors in console, nothing and this make harder to debug the code.

Ok, the problem is that “this.addName()” function is executed immediately because the parenthesis and Javascript read the code from top to bottom and when read that part then the method is executed.

So, we don’t want to execute that function immediately, we want to listen the click event and then execute that function (this.addName()) only when that event occur. Ok, in this case we can remove the parenthesis and this will solve the problem 🙂

An this is solved because I’m just passing a reference to that function that’s it and when the click happens then that function is executed.

btn.addEventListener('click', this.addName); // Listen when user click the button 

Result:

Ok, Now our code works but sometime “this” can behave strange, so let’s change a little beat our code.

Now, let’s suppose that we have a list of names (array) inside the NameGenarator class and we want to cycle to that names when user click the button.

// Class to generate name when user click the HTML button
class NameGenerator {
    // Initialization
    constructor() {
        const btn = document.querySelector('button'); // Selecting the button 

        const names = ['carlos', 'efrain', 'karolina']; // array with names

        btn.addEventListener('click', this.addName); // Listen when user click the button 
    }

    // Method of the NameGenerator class 
    addName() {
        const name = new NameField('Cristina'); // Creating a new NameField object with 'Cristina' as name
    }
}

So, in addName() method I need to have access to that array because is where here I’m using every name.

To do the array names accessible to all the class we need to use this in the constructor, so instead of be a variable will be a property of the class, like:

this.names = ['carlos', 'efrain', 'karolina']; // array with names

And then in my method addName() I can use the array like:

    // Method of the NameGenerator class 
    addName() {
        const name = new NameField(this.names); // passing the array
    }

Ok, cool. Now because we want to cycle this array we need to declare a property (inside the constructor) that handle the index of every item in the array, will be like this:

    // Initialization
    constructor() {
        const btn = document.querySelector('button'); // Selecting the button 

        this.names = ['carlos', 'efrain', 'karolina']; // array with names

        this.currentName = 0; // To handle every position in the array names

        btn.addEventListener('click', this.addName); // Listen when user click the button 
    }

Nice, so now we can use the this.currentName property in method addName() and a validation to not exceed the names array.

    // Method of the NameGenerator class 
    addName() {
        const name = new NameField(this.names[this.currentName]); // passing the array
        this.currentName++;

        if (this.currentName >= this.names.length) { // Validation to not exceed the array elements 
            this.currentName = 0;
        }
    }

Ok, let’s run this in our browser:

oh! ooh! we have a problem “Cannot read property ‘undefined’ of undefined”.

So, “this” does not alway refer to the surrounding object (to the class), It’s generally refers to that but not always.

“this” keyword refer to who ever calls the function in which we use the “this” keyword

So, in the function addName() we are using “this” but who is calling the function addName() or who is the responsible for execute addName() method?

Who call or execute the addName() function is the button (when user click the button)

btn.addEventListener('click', this.addName); // Listen when user click the button 

So, “this” here refers to the button it self, not to the class and this show us that “this” do not always refer the class or object, instead of “this” will refer to the thing that calls your code in this case the “button”

If we insert console.log of “this” inside the addName() method

    // Method of the NameGenerator class 
    addName() {
        console.log(this);
        ....
    }

We can see that when the user click the button then the button itself appears on the console and that is why we have “undefined” errors.

Ok, how can we solve this problem?

1.- One way to solve this problem is:

Well we can use a special method called .bind() and this method allow us to tell Javascript what “this” should be referring to in the function that will eventually executed.

class NameGenerator {
    // Initialization
    constructor() {
        const btn = document.querySelector('button'); // Selecting the button 

        this.names = ['carlos', 'efrain', 'karolina']; // array with names

        this.currentName = 0; // To handle every position in the array names

        btn.addEventListener('click', this.addName.bind(this)); // Listen when user click the button 
    }
    ...
}

In this case this.addName.bind(this) will refer to the constructor and the constructor will refer to the class and not to the button.

So with this.addName.bind(this) I’m telling Javascript that not matter who execute the addName() function – the “this” inside addName() should always refer to the constructor “this“.

Result:

2.- Second solution is use a arrow function and will work; the reason for that is that the arrow functions solve that problem of the behavior of “this”, arrow functions basically keep the context of “this

        btn.addEventListener('click', () => {
            this.addName();
        }); 

Result:

ENTIRE CODE

// Class to create <li> element and append that element to the <ul>
class NameField {
    // Initialization
    constructor(name) {
        const field = document.createElement('li'); // Creating <li> element
        field.textContent = name; // Adding content
        const nameListHook = document.querySelector('#names'); // Selecting the <ul> element
        nameListHook.appendChild(field);
    }
}

// Class to generate name when user click the HTML button
class NameGenerator {
    // Initialization
    constructor() {
        const btn = document.querySelector('button'); // Selecting the button 

        this.names = ['carlos', 'efrain', 'karolina']; // array with names

        this.currentName = 0; // To handle every position in the array names

        btn.addEventListener('click', () => {
            this.addName();
        }); // Listen when user click the button 
    }

    // Method of the NameGenerator class 
    addName() {
        const name = new NameField(this.names[this.currentName]); // passing the array
        this.currentName++;

        if (this.currentName >= this.names.length) { // Validation to not exceed the array elements 
            this.currentName = 0;
        }
    }
}

const gen = new NameGenerator();

By Cristina Rojas.