How Does The JavaScript “this” Keyword Work?

The this keyword is a fundamental concept in JavaScript, but it can be one of the most confusing aspects for both new and experienced developers.

In this tutorial, we will explore the behaviour of the this keyword in various contexts which helps to write effective JavaScript code, as it determines the context in which a function is executed.

The “this” Keyword

In JavaScript, the this keyword refers to the current object, or more precisely, the object that is executing the current function. The value of this is determined by how a function is called, and it can change dynamically during runtime.

Here are some key points about “this” in JavaScript:

Table of Contents #
  1. Global Context
  2. Function Context
  3. “this” Circumstances
  4. Common Pitfalls
  5. Best Practices

1. Global Context:

When used outside of any function, this typically refers to the global object. In a browser environment, the global object is window, while in Node.js, the global scope of a module is the module itself.

console.log(this === window); // true (in a browser environment)

2. Function Context:

Inside a function, the value of this depends on how the function is called. When a function is called as a regular function (not as a method of an object), `this` points to the global object.

Regular Function Call:

When a function is called as a regular function (not as a method of an object), this points to the global object.

function sayHello() {
    console.log(this === window);
}
sayHello(); // true

Method Call:

When a function is called as a method of an object, this refers to the object that contains the method.

const person = {
    name: 'John',
    greet: function () {
        console.log('Hello, ' + this.name);
    },
};

person.greet(); // "Hello, John"

Constructor Call:

When a function is used as a constructor to create an instance of an object using the “new” keyword, this refers to the newly created object.

function Person(name) {
    this.name = name;
}

const john = new Person('John');
console.log(john.name); // "John"

Event Handlers:

In event handler functions, such as those used with HTML elements, this typically refers to the element that triggered the event.

document.getElementById('myButton').addEventListener('click', function () {
    console.log(this.id); // "myButton"
});

Arrow Functions:

Arrow functions have a lexical this, which means they capture the value of this from their surrounding context. They do not have their own this binding.

Or you can say arrow functions do not have their own “this” context. They inherit the “this” value from the surrounding scope.

const person = {
    name: 'Alice',
    greet: () => {
        console.log('Hello, ' + this.name);
    },
};

person.greet(); // "Hello, undefined" (this is not bound to the person object)

In this example, the greet method which is an arrow function takes the this value from the surrounding scope that is the window object in a browser. since the window object does not have a name property, you will see undefined.

3. “this” Circumstances

In the following example, the this of the greet function is refers to the person object, but the this of the test function refers to the global object which is window in a browser.

const person = {
    name: 'Alice',
    greet: function () {
        console.log('greet function:', this); // "person" Object

        function test() {
            console.log('test function:', this); // "global" Object (window)
        }
        test(); // calling as a regular function
    },
};

person.greet(); // calling as a method of the person object

Why? Because we calling the greet function as a method of the person object, on the other hand we calling the test function as a regular function.

Always remember how a function is called

Here, the this of the greet function is refers to the global object (window in a browser) because we calling the greet function through the test() as a regular function.

const person = {
    name: 'Alice',
    greet: function () {
        console.log(this);
    },
};

const test = person.greet;

test(); // "global" object (window)

4. Common Pitfalls

Understanding and managing this can be tricky. Some common pitfalls include:

  • Losing the value of this when passing methods as callbacks without proper binding or arrow functions.
  • Confusion when working with nested functions and nested objects, as this can change in inner functions.

5. Best Practices

To avoid common pitfalls and write more reliable code:

  • Be aware of the different contexts in which this behaves and understand the rules governing its behavior.
  • Use the bind method to explicitly set the value of this for a function.
  • Use arrow functions when you want to capture the value of this from the surrounding context.