Back

Explain how `this` works in JavaScript

🌳 Advanced
JS
5m

The this keyword always refers to the current context of a function or script.

this is a confusing topic for most of us (pun intended), but it doesn't have to be. All you need to do is remember some rules.

Following rules, in order of precedence, dictate how value of this is determined during runtime:

Usage in a Function constructor

If a function is called with the new keyword, this inside the function refers to the newly created object instance.

function Book(title) { console.log(this); this.title = title; console.log(this); } const book1 = new Book("Jungle Book"); // {} // { title: 'Jungle Book' } console.log(book1.name); // 'Jungle Book'

Explicitly binding this

call(), apply() or bind() can be used to explicitly set the value of this inside a function.

  • call() and apply() are used when a function needs to be invoked immediately.
  • bind() returns a new function to be used later.
const obj = { name: "Ben" }; function sayHello() { console.log(`Hello, ${this.name}!`); } const sayHelloToBen = sayHello.bind(obj); sayHelloToBen(); // Hello, Ben! sayHello.call(obj); // Hello, Ben! sayHello.apply(obj); // Hello, Ben!

Usage in a method call

If a function is a method of an object, then this refers to the object.

const person = { name: "Ben", logThis: function () { console.log(this); }, }; person.logThis(); // { name: 'Ben', logThis: fn() }

Usage in a Regular function call (Free function invocation)

If a function is invoked in the global context, then this refers to the global object(in non-strict mode) or undefined(in strict mode).

For browsers, the global object is window.

// Browser function showThis() { console.log(this); } showThis(); // Window { open: fn, alert: fn, ... }

When a function is declared in the global context, it becomes a property of the window object. Calling window.showThis() would yield the same result. This is why it's an implicit method invocation. (Refer the rule above this one)

If multiple rules apply, then the rule with higher precedence will apply.

const obj1 = { value: 1, showThis: function () { console.log(this); }, }; const obj2 = { value: 2 }; obj1.showThis.call(obj2); // { value: 2 }

In the example above, two rules are being applied: Method invocation and Explicit binding. As explicit binding has higher precedence, it gets to set the value of this.

Arrow functions

Arrow functions don't follow the rules stated above as they don't have a this value of their own. They pick the this value from the parent scope.

const person = { name: "Ben", showThis: () => { console.log(this); }, showThisWrapped: function () { const arrowFn = () => console.log(this); arrowFn(); }, }; person.showThis(); // Window { open: fn, alert: fn, ... } person.showThisWrapped(); // { name: 'Ben', showThis: fn, showThisWrapped: fn }

This is why you should avoid using arrow functions for event listeners. Event listeners bind the HTML element to the this value but if the handler is an arrow function, it is not possible.

document.querySelector("#cta").addEventListener("click", function () { console.log(this); // HTMLButtonElement }); document.querySelector("#cancel").addEventListener("click", () => { console.log(this); // Window });

Summary

  1. In a function constructor(new Person()), this will be the newly created object instance.
  2. If explicitly bound using bind(), call() or apply(), this inside the function will be set to the value provided.
  3. Inside a method, this is set to the object that method is a property of.
  4. Inside a free function invocation, this points to global object(non-strict mode) or undefined(strict mode).
  5. If multiple rules apply, the rule stated first(1-4) will apply.
  6. Arrow functions don't have their own this. Its value is picked from the parent scope.

Resources

MDN: this