1

I'm reading this article on using ES6 arrow functions. It gives the following example, where you have to use bind(this), followed by the corresponding code with arrow functions.

var obj = {
  id: 42,
  counter: function counter() {
    setTimeout(function() {
      console.log(this.id);
    }.bind(this), 1000);
  }
};

It says In the ES5 example, .bind(this) is required to help pass the this context into the function.

What I Want To Know: Why do you use bind(this) with the callback to setTimeout rather than with the counter function? ie why isn't the above code like this:

var obj = {
  id: 42,
  counter: function counter() {
    setTimeout(function() {
      console.log(this.id);
    }, 1000);
  }.bind(this);
};
gkeenley
  • 6,088
  • 8
  • 54
  • 129

1 Answers1

2

Why do you use bind(this) with the callback to setTimeout rather than with the counter function?

Because the counter function (which works like a method of the obj object) already has the proper this because you call it like obj.counter() so it gets this from calling it as obj.counter(). Assuming you call counter as obj.counter(), then if you do console.log(this.id) on the first line of the counter() function, it will properly show the id value.

The callback you pass to setTimeout() however has no natural this value unless you use an arrow function or .bind() on the callback function itself because when setTimeout() calls your callback it does not set a specific this value (it just calls your callback as a normal function), therefore the this value goes to the default. That means this will be undefined if running strict mode or the global object if running in loosey-goosey mode inside of the setTimeout() callback.

See the 6 ways that the value of this is set here when calling a function.


I should also mention that if you did what you were proposing like this:

var obj = {
  id: 42,
  counter: function counter() {
    setTimeout(function() {
      console.log(this.id);
    }, 1000);
  }.bind(this);
};

Not only would it not help the setTimeout() callback at all, but it would also bind the wrong value of this to the counter() method. You would get whatever this was before the var obj definition (also known as the lexical value of this).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Might be worth mentioning that functions defined like this within objects are often referred to as _methods_. Kinda helps contextualise their usage / scope a bit better IMO – Phil Jun 02 '20 at 01:15
  • Got it, thanks! And thanks @Phil as well for your note. – gkeenley Jun 02 '20 at 01:26
  • Actually wait, I still don't understand why using `bind` on the callback sets the value of `this` to `obj`, whereas using it on `counter` sets it to the lexical value. Why doesn't applying it to `counter` bind `counter` to `obj`? – gkeenley Jun 02 '20 at 01:30
  • @gkeenley - Because calling `obj.counter()` already sets `this` inside of `counter` to `obj`. So, when you then use `function() {}.bind(this)` with `setTimeout()`, it is picking up that value of `this` (the desired one). Doing `.bind(this)` to counter itself, takes the `this` value that was in play at that time and sets that for the `counter()` scope. That has two problems. First, it's the wrong value of `this` (it is not `obj`) and second, it controls the counter scope, not the callback for `setTimeout()` scope which is where your issue is. – jfriend00 Jun 02 '20 at 01:40
  • Ok, all is understood. I appreciate it. – gkeenley Jun 02 '20 at 02:14