Why is "this" undefined in the method of this class?

I tried to search all over the internet, but I'm still annoyed by the problem with the JS class I'm writing for Micro Service (still learning a bit).

So, I'm trying to call a class method on an instance of an object, and, according to my knowledge and my (erroneous, I assume) unit tests, it should work.

Well, I'll start with the error I get:

GET /api/users 500 2.863 ms - 2649 TypeError: Cannot read property 'repository' of undefined at list (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\controllers\user-controller.js:20:9) at Layer.handle [as handle_request] (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\node_modules\express\lib\router\layer.js:95:5) at next (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\node_modules\express\lib\router\route.js:137:13) 

(And much more).

Code call code:

 user-controller.js 'use strict'; var utils = require('./utils'); class UserController { constructor(repository) { this.repository = repository || {}; } /** * * Lists all users. * * @param {object} req * @param {object} res */ list(req, res) { this.repository.list(function (err, users) { if (err) return res.status(500).json(utils.createError(500)); if (Object.keys(users).length !== 0) { res.json(users); } else { res.status(404).json(utils.createNotFound('user', true)); } }); } // more code } module.exports = UserController 

Calling controller

 user-api.js 'use strict'; var express = require('express'); var UserController = require('../controllers/user-controller'); var router = express.Router(); module.exports = function (options) { var userController = new UserController(options.repository); router.get('/users', userController.list); // Mode code return router; }; 

I really don't know why this undefined in UserController .

Any help would be very helpful.

+5
source share
2 answers

When you do this:

 router.get('/users', userController.list); 

what is passed to your router is just a reference to the .list method. The userController instance is lost. This is not unique to routers - it is a common property of how things are passed in Javascript. To further understand what you basically do is:

 let list = userController.list; // at this point the list variable has no connection at all to userController router.get('/users', list); 

And, in Javascript strict mode, when you call a regular function without reference to an object, for example the list() call above, then this will be undefined inside the function. This is what happens in your example. To fix this, you must ensure that your method is called with the proper object reference, as in userController.list(...) , so that the interpreter sets this accordingly.

There are several ways to solve this problem:

Create your own wrapper of functions

 router.get('/users', function(req, res) { userController.list(req, res); }); 

This works in any version of Javascript.


Using .bind() to make a wrapper for you that calls it with the correct object

 router.get('/users', userController.list.bind(userController)); 

This works in ES5 + or with .bind() polyfill.


Use the ES6 arrow label

 router.get('/users', (...args) => userController.list(...args)); 

It works in ES6 +


Personally, I prefer the .bind() implementation, because I think it is simply simpler and more declarative / understandable than any other, and the ES6 "shortcut" is not much shorter.

+9
source

router.get() will not call your class the way you think. You give it a reference to the function that it is called in the context of the router.get value, it will not be in the context of your userController.

You can fix this by doing:

 router.get('/users', function(){userController.list(...arguments)}); 

In other words, do not use the userController link with the list expression, use express, using the closure that the userController will have a list call with the given arguments.

+2
source

Source: https://habr.com/ru/post/1270800/


All Articles