3

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?

Bharata
  • 13,509
  • 6
  • 36
  • 50
Moshe Binieli
  • 343
  • 3
  • 21
  • 1
    `setInterval(() => this.foo(), ...)` or `setInterval(this.foo.bind(this), ...)` or ... This is nothing to do with Angular/TS/ES6, see any JS `this` scoping question. – jonrsharpe Jan 14 '18 at 22:45

2 Answers2

6

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

Bharata
  • 13,509
  • 6
  • 36
  • 50
Ofir G
  • 736
  • 6
  • 18
2

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.

Bharata
  • 13,509
  • 6
  • 36
  • 50
williamsandonz
  • 15,864
  • 23
  • 100
  • 186