Blog>
Snippets

The Pitfalls of 'this' in Callbacks

Show a common mistake of 'this' losing its context inside a callback, and how to preserve it using a variable or an arrow function.
function Button() {
  this.clicked = false;
  this.click = function() {
    // Intending to set 'this.clicked' to true
    setTimeout(function() {
      this.clicked = true; // 'this' does not refer to Button instance here
      console.log(this.clicked); // Outputs 'undefined' or 'true' on the global/window object
    }, 1000);
  };
}

var myButton = new Button();
myButton.click();
This JavaScript code attempts to simulate a button click event where it sets 'clicked' property to true after a timeout. However, the 'this' keyword inside the setTimeout callback function does not refer to the Button instance, but to the global context (or undefined in strict mode). This results in the 'clicked' property not being set as expected on the Button instance.
// Solution using a cached 'this' variable
function FixedButton() {
  this.clicked = false;
  this.click = function() {
    var self = this; // 'self' now refers to the FixedButton instance
    setTimeout(function() {
      self.clicked = true; // 'self' is used instead of 'this'
      console.log(self.clicked); // Outputs 'true'
    }, 1000);
  };
}

var fixedButton = new FixedButton();
fixedButton.click();
The issue from the previous code is resolved by caching the 'this' keyword into a variable named 'self'. The 'self' variable is used within the setTimeout callback function to correctly refer to the FixedButton instance, allowing the 'clicked' property to be set as intended.
// Solution using an arrow function
class ArrowButton {
  constructor() {
    this.clicked = false;
  }
  click() {
    setTimeout(() => {
      this.clicked = true; // The arrow function preserves 'this' from the enclosing context
      console.log(this.clicked); // Outputs 'true'
    }, 1000);
  }
}

const arrowButton = new ArrowButton();
arrowButton.click();
This code refactors the Button example using an ES6 class. It demonstrates how to use an arrow function to preserve the context of 'this' within the setTimeout callback. The arrow function does not have its own 'this', so 'this' refers to the ArrowButton instance as expected.