1

I've just read the book "Scope and Closures" of the series You Don't Know Javascript. The book has an appendix talking about Lexical this. It gives an example of how this can lose its binding and how can be solved using arrow functions.

I'm having problems with the example of the first situation. This is the example of the book:

var obj = { id: 2, cool: function(){console.log(this.id)}}
id=3;
obj.cool();
setTimeout(obj.cool, 100);

This works fine an prints the expected result: 2 the first time, and 3 in the setTimeout call.

But if I change this call by:

var obj = { id: 2, cool: function(){console.log(this.id)}}
id=3;
obj.cool();
setTimeout(function(){obj.cool()}, 100); 

It prints 2 both times.

My guess is that, by enclosing the call to obj.cool inside a function, I'm creating a scope where id has the expected value.

Am I right? Is this the right explanation? Am I missing something?

vgaltes
  • 890
  • 9
  • 15
  • No, you're not creating a scope with an extra `id`. You just call the `obj.cool` on `obj` now in the new function. – Bergi Mar 06 '18 at 23:12
  • Possible duplicate of [How does the "this" keyword work?](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – melpomene Mar 06 '18 at 23:13
  • Can you explain further why `2` and `3` are the expected results in the first example? I guess there's some flaw in your understanding of that as well. – Bergi Mar 06 '18 at 23:13
  • @Bergi you can find the explanation here: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/apC.md – vgaltes Mar 06 '18 at 23:22
  • 1
    `obj.cool();` calls the `cool` function with the context of `obj`. `obj.cool` passes just the function to `setTimeout` without the context of `obj`. When it's called it's not associated with `obj` any more it's just a plain function so it doesn't know to use `obj` as `this`. – Rocky Sims Mar 06 '18 at 23:24
  • @vgaltes Sure I can find it (if I needed it), I was asking whether *you* understood it and could explain it. – Bergi Mar 06 '18 at 23:48

1 Answers1

3

Not exactly. In the first example setTimeout calls your cool function not as a method, but as a simple function. That's why you "inherit" a "global" scope and id is equal to 3.

If you call a function with . notation, like in a second example - your call scope is whatever you put before . and since obj.id === 2 - you see 2 printed.

The key to understand this is: setTimeout gets a reference to just a function. It doesn't know that your function is a method of an object. It doesn't know anything about this object either. All it gets is just a reference to a "code to execute". In the second scenario you explicitly tell to an interpreter that you want to execute function cool as a method of the object obj.

Andrew Kovalenko
  • 4,873
  • 2
  • 24
  • 40
  • I think you're right. It's the same explanation that @rocky-sims gave in a comment and it makes sense to me. Thanks! – vgaltes Mar 06 '18 at 23:29
  • 1
    As a side note, you can use also [`Function.prototype.bind`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) to create a new function that has `this` bound to `obj` instead of using an anonymous function to call the method: `setTimeout(obj.cool.bind(obj), 100);`. `bind` can also be used to create [partially applied functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Partially_applied_functions). – Useless Code Mar 07 '18 at 03:16