1

I'm aware that the subject of "this" in both JS and TS has been covered by many questions, but I haven't been able to find an answer to the specific issue I'm facing. I'm certain that I'm just not understanding something basic and it's tricky to google for amongst the mountain of questions that boil down to arrow function scoping rules.

To preface, the code I'm struggling with is running on a Node back-end.

So, I've created a controller class with a method defined not as an arrow function (simplified for demonstration):

class UserController extends AbstractController {

    constructor() { ... }

    public async getUsers(x, y): Promise<any> {
        return this.processRequest( ... ); //processRequest is a protected async definition on AbstractController
    } 
}

// ...
export default new UserController()

The issue I'm having is that this inside getUsers() is undefined, where I'd naturally expect it to be the instance of UserController it's executed on.

If I do convert getUsers to an arrow function...

class... {
    public getUsers = async (x, y): Promise<any> => {
        return this.processRequest( ... );
    }
}

...then it works as expected.

I believe I understand how arrow function this captures work with regards to the surrounding scope; but in this case I feel like I'm missing two key understandings:

  • why does the first method not have any this? I feel like this should be the instance of UserController, and if not then at least it should be some more global object.
  • why does the second method work at all? There is no "surrounding scope" to capture this from - is there?

Here's how getUsers() is called:

import UserController from 'user.controller';

userRouter.get('/', authMiddleWare, UserController.getUsers);
  • 3
    The issue is where you **call** `getUsers`, which you haven't shown us. – T.J. Crowder Jun 19 '19 at 11:53
  • *"why does the second method work at all? There is no "surrounding scope" to capture this from - is there?"* Yes, there is: What you've done there is an initializer on a public property. `this` for a property initializer is what `this` is in a constructor. (In fact, property initializers are relocated to the constructor, just after the call to `super` if any.) [Details here](https://stackoverflow.com/a/48921502/157247). – T.J. Crowder Jun 19 '19 at 11:55
  • 1
    When you look at how you're calling `getUser`, you'll almost certainly find that the issue is covered by [this question's answers](http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback) (most likely) and/or [this one's](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) (more generally). :-) – T.J. Crowder Jun 19 '19 at 11:57
  • Thanks guys, I've updated the question with some more information. – Jonnopon3000 Jun 19 '19 at 12:04
  • 1
    `UserController.getUsers `should be `UserController.getUsers.bind(UserController)`. See linked question from TJ. Otherwise, you are passing a loose reference to a non arow function, which is why this is undefined. It would be the global object of you weren't in strict mode. If you read the questions carefully, you should be able to understand – Juan Mendes Jun 19 '19 at 12:09
  • 1
    This is a duplicate of [this question](http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback), as I mentioned above. **Strongly** recommend using standard TypeScript/JavaScript naming conventions, e.g. `import controller from 'user.controller'` rather than using the *class's* name for an *insatnce*. That just misleads anyone trying to read the code. – T.J. Crowder Jun 19 '19 at 12:33
  • Thanks guys, I understand what's going on a lot better now and can move forward. @T.J.Crowder is technically right, this appears to be a duplicate. – Jonnopon3000 Jun 19 '19 at 12:38
  • @T.J.Crowder point about naming convention noted. As a general question if I may, would it be "better" in your opinion for me to convert instance methods on the UserController to arrows, or to use the `bind` approach for passing them as callbacks? I'm here nor there, but I would lean towards not having to use arrows for instance methods as a note of readability for the class on its own. – Jonnopon3000 Jun 19 '19 at 12:44
  • @Jonnopon3000 - Lots of people do. The argument against doing that is that it makes doing unit tests on your classes hard, because unit test frameworks often work by replacing methods on the prototypes of the classes. When you use an arrow function, there is no method on the prototype. So instead for that sort of thing, the pattern I prefer is either to `bind` on usage (as Juan mentioned), or if doing that would mean calling `bind` repeatedly, do it once in the constructor: `this.getUsers = this.getUsers.bind(this);`. – T.J. Crowder Jun 19 '19 at 12:47
  • define a variable in class like this let self =this; now you can use the self inside your function – NIVINCEN Jun 19 '19 at 13:05

0 Answers0