Mauricio Robayo's Blog

Mauricio Robayo's Blog

JavaScript `this` inside `setInterval`

image.png

When the function is called, it is called using the new keyword. One of the things the new keyword does is that it "binds the newly created object instance as the this context".

We can test the following snippet of code to verify the value of this when the function is called with and without the new keyword:

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

console.log("Without using the new keyword the value of `this` is:");
someFunction(); // here `this` is the global object in node or window in the browser
console.log("Using the new keyword the value of `this` is:");
new someFunction(); // here `this` is the function itself

Since stopwatch is called using the new keyword, this refers to the function itself, otherwise it would refer to the global (nodejs) or window (browser) object.

In JavaScript, all functions are objects, so the first this, the one outside the setInterval, refers to the stopwatch function, and we are creating the time property in that stopwatch function (object):

function stopwatch() {
  this.time = 0;
  console.log("`this` inside stopwatch", this);

  const interval = setInterval(function () {
    this.time++;
    console.log(this.time);
  }, 1000);
}

const watch = new stopwatch();

In the case of the interval, the callback function is called by setInterval, and the value of this is the interval itself. If we log this inside the setInterval callback we can check the value of this:

function stopwatch() {
  this.time = 0;

  setInterval(function () {
    this.time++;
    console.log("`this` inside setInterval:", this);
    console.log(this.time);
  }, 1000);
}

const watch = new stopwatch();

So, the issue in the first snippet is that we have different this, one referencing the stopwatch with the time property, and one referencing the interval without the time property.

To reference the stopwatch inside the setInterval, we can assign the stopwatch (the first this) to a variable and then use that variable that is referencing the stopwatch inside the setInterval (that's what the second snippet of code is doing):

function stopwatch() {
  const stopwatchReference = this;
  stopwatchReference.time = 0;

  setInterval(function () {
    stopwatchReference.time++;
    console.log(stopwatchReference.time);
  }, 1000);
}

const watch = new stopwatch();

Another way to solve it is to bind the callback to the stopwatch this:

function stopwatch() {
  this.time = 0;

  setInterval(
    function () {
      this.time++;
      console.log(this.time);
    }.bind(this),
    1000
  );
}

const watch = new stopwatch();

We could also pass this as a third argument to setInterval, which will then be passed to the callback:

function stopwatch() {
  this.time = 0;

  setInterval(
    function (that) {
      that.time++;
      console.log(that.time);
    },
    1000,
    this
  );
}

const watch = new stopwatch();

All the above are fine solutions to correctly set this inside the setInterval, but the preferred way nowadays is to use an arrow function, which does not have its own bindings to this and will use the this in the enclosing lexical scope:

function stopwatch() {
  // Whatever this is here...
  this.time = 0;

  setInterval(() => {
    // ...is what this is going to be here.
    this.time++;
    console.log(this.time);
  }, 1000);
}

const watch = new stopwatch();
 
Share this