0

I was going through this article and couldn't understand their solution 1 to losing context:

let user = {
  firstName: "John",
  sayHi() {
    alert(`Hello, ${this.firstName}!`);
  }
};
// Problem: 
setTimeout(user.sayHi, 1000); // Hello, undefined!

// Solution 1:
setTimeout(function() {
  user.sayHi(); // Hello, John!
}, 1000);


In the second case, I understand that this works because the anonymous function callback gets access to a lexical environment populated with the globals (here, user) but then even user.sayHi() should have a similar lexical environment with access to the stuff between the surrounding {} (i.e. firstName) so we shouldn't need the wrapper in the first place. What am I missing here?

  • `this` binding is nothing to do with lexical scope - it's a completely separate mechanism. The important thing is the "call site" - where the function is called from, and crucially, how. Here it works because it's called as `user.sayHi()`, the `user.` in front essentially tells the function to use `user` as its `this`. Whereas in the example in the article with `setTimeout(user.sayHi, 1000)`, it's the browser itself that ends up calling that function, and it "loses the context" of being part of the `user` object. – Robin Zigmond Jul 18 '21 at 15:39
  • It's a tricky area, I would recommend reading [this](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes) (especially Chapter 2) for a much deeper understanding. – Robin Zigmond Jul 18 '21 at 15:39
  • 1
    The value of `this` is ***not*** lexically determined for normal functions (and shorthand methods like `sayHi`). Instead, the value of `this` is determined *at call time*. If you execute `user.sayHi()` then `this = user`. As a rule of thumb, it's the value before the last dot. If you have `a.b.c.f()` then `f` will be executed with `this = a.b.c`. If there is nothing before the last dot, then `this = undefined` (and in sloppy mode that's substituted for `window`). Passing `setTimeout(user.setHi, 1000)` will then execute just the function reference as `fn()` thus losing `this`. – VLAZ Jul 18 '21 at 15:39
  • 1
    You're mixing the concepts of scope and binding. Binding is NOT scope and scope is not binding. Scope is about lexical environment - what variables are visible and what re not. Binding is about what object is bound to `this`. In this case the rule is simply that `this` is the object before the last **dot**. So when you call `user.sayHi()` the `this` points to `user`. But when you pass do `setTimeout(user.sayHi,1000)` you are actually doing `sayHi = user.sayHi; setTimeout(sayHi,1000)`. So there is no more **dot** which means `this` is either the global object or undefined (strict mode) – slebetman Jul 18 '21 at 15:41

0 Answers0