Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - setInterval with passed function does not recognize global variables

I was stuck in some weird problem and then the problem got a challenge to understand why Angular behaves like this, there was no much luck searching at Google for the problem, so I am here.

I wanted to test setInterval() function and counter some variable, not something too hard, and I got stuck in problem that I couldn't find solution for or explaination.

This is my code I am using and it works fine:

public counter: number = 1;

  myFunction() {
    setInterval(() => {
      console.log(this.counter);
      this.counter++;
    }, 1000);
  }

Output: 1, 2, 3, 4, 5...

This code works fine, but when I am taking the function to be like this, I get undefined as output and then Nan, Nan, Nan.

public counter: number = 1;

  foo() {
    console.log(this.counter);
    this.counter++;
  }

  myFunction() {
    setInterval(this.foo, 1000);
  }

* myFunction is the starting function *

Output: undefined, Nan, Nan, Nan...

I guess there is some problem with variables access that foo() cannot access the global variables, but how is that happening? and how can I fix this problem?

like image 281
Moshe Binieli Avatar asked Oct 23 '25 16:10

Moshe Binieli


2 Answers

here's a code that acts as you discribed

 class A{
 public counter: number = 1;

   foo() {
     alert(this.counter);
     this.counter++;
   }

   myFunction() {
     setInterval(this.foo, 1000);
   }
 }

 const a = new A();
 a.myFunction(); // output andefined, Nan, Nan ...

now the code using bind : JSFiddle

 class A{
 public counter: number = 1;

   foo() {
     alert(this.counter);
     this.counter++;
   }

   myFunction() {
     setInterval(this.foo.bind(this), 1000);
   }
 }

 const a = new A();
 a.myFunction(); // output 1, 2, 3, 4 ...

this is a very tricky subject so

first check this question How to access the correct 'this' inside a callback?

and couple of insanely good answer about the subject here and here .


now the bind method - Function.prototype.bind() :

(from the docs)

The bind() 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.

and loved this expletetion (took from the Examples part in the docs) :

The simplest use of bind() is to make a function that, no matter how it is called, is called with a particular this value.

A common mistake for new JavaScript programmers is to extract a method from an object, then to later call that function and expect it to use the original object as its this (e.g. by using that method in callback-based code). Without special care, however, the original object is usually lost.

Creating a bound function from the function, using the original object, neatly solves this problem

like image 196
Ofir G Avatar answered Oct 26 '25 06:10

Ofir G


In the first example you are passing a function declared in es6 shorthand (link here), therefore "this" will be bound to the current scope.

In the second example you are passing a reference to a function and because setTimeout executes the function with this pointing to the global object, "this" equals the window object and therefore the properties are undefined.

like image 44
williamsandonz Avatar answered Oct 26 '25 04:10

williamsandonz