JavaScript `this` binding in common contexts

In JavaScript, the this keyword is very flexible and confusing, evaluated at runtime, can have different values depending in which context it appears, including but not limited to: function, class, global, and strict mode.

A common mistake for new JavaScript developers is to extract a method from an object, then later call that function and expect it to use the original object as its this, like using the method in callback-based code.

Here are common contexts in which this behaves differently:

  1. Global context: When a function is executed in the global context, the reference of unbound this follows the rule of “global object in non-strict, undefined in strict”.
function foo() {
  console.log(this);
}

foo(); // "window" in browsers, "global" in Node.js

In “strict mode”, the value of this is always undefined.

'use strict';

function foo() {
  console.log(this);
}

foo(); // undefined
  1. Object method context: A function that is a property of an object is called its method, when a method is executed, this refers to the object before dot, the one used to call the method.
const person = {
  name: 'John',
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

person.greet(); // Output: Hello, my name is John
  1. Event context: When an event occurs in a web page, the value of this refers to the HTML element that the event occurred on.
const button = document.querySelector('button');

button.addEventListener('click', function() {
  console.log(this === button); // Output: true
});
  1. Constructor context: When a function is used as a constructor to create a new object, this refers to the newly created object.
function Person(name) {
  this.name = name;
}

const john = new Person('John');
console.log(john.name); // Output: John
  1. Using bind(): This method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
const person = {
  name: 'John',
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const greet = person.greet.bind({ name: 'Jane' });
greet(); // Output: Hello, my name is Jane
  1. Using call() : This method calls the function with a given this value and arguments provided individually.
const person = {
  name: 'John',
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

person.greet.call({ name: 'Jane' }); // Output: Hello, my name is Jane
  1. Using apply(): This method calls the specified function with a given this value, and arguments provided as an array or an array-like object.
const person = {
  name: 'John',
  greet(city) {
    console.log(`Hello, my name is ${this.name} and I live in ${city}`);
  }
};

person.greet.apply({ name: 'Jane' }, ['New York']); // Output: Hello, my name is Jane and I live in New York
  1. Using arrow functions: They do not have their own this value, but inherit it from the enclosing lexical context.
const person = {
  name: 'John',
  greet: () => {
    console.log(`Hello, my name is ${this.name}`);
  }
};

person.greet(); // Output: Hello, my name is undefined