简体   繁体   English

为什么在这个类方法中“this”未定义?

[英]Why is “this” undefined in this class method?

I've tried to search over what seems to be the entire internet, but I'm still vexed by a problem with a JS class I'm writing for a Micro Service (still in learning a bit). 我试图搜索似乎整个互联网的内容,但我仍然对JS类的问题感到烦恼,我正在为微服务写作(仍在学习中)。

So, I try to call a class method on an instantiated object, and according to my knowledge and my (faulty I presume) unit tests it should work. 所以,我尝试在实例化的对象上调用一个类方法,并根据我的知识和我的(我猜错的)单元测试它应该工作。

Well, I'll start with the error I receive: 好吧,我将从收到的错误开始:

    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 a lot more). (还有更多)。

The code calling 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

Caller of 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 jave no idea on why this undefined in UserController . 我真的不知道为什么thisUserController未定义。

Any help would be greatly appriciated. 任何帮助都会受到极大的关注。

When you do this: 当你这样做:

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

what gets passed to your router is just a reference to the .list method. 传递给路由器的只是对.list方法的引用。 The userController instance gets lost. userController实例丢失了。 This is not unique to routers - this is a generic property of how things are passed in Javascript. 这不是路由器所特有的 - 这是Javascript中传递内容的通用属性。 To understand further, what you are essentially doing is this: 要进一步了解,您实际上在做的是:

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

And, in Javascript's strict mode, when you call a regular function without any object reference such as calling list() above, then this will be undefined inside the function. 而且,在Javascript的strict模式下,当你调用一个没有任何对象引用的常规函数​​时,如上面的调用list() ,那么this将在函数内部undefined That is what is happening in your example. 这就是你的例子中发生的事情。 To fix it, you need to make sure that your method is called with a proper object reference as in userController.list(...) so that the interpreter sets the this value appropriately. 要修复它,您需要确保使用适当的对象引用调用您的方法,如userController.list(...)以便解释器适当地设置this值。

There are multiple ways to solve this problem: 有多种方法可以解决此问题:

Make your own function wrapper 制作自己的函数包装器

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

This works in any version of Javascript. 这适用于任何版本的Javascript。


Using .bind() to make a wrapper for you that calls it with the right object 使用.bind()为您创建一个使用正确对象调用它的包装器

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

This works in ES5+ or with a .bind() polyfill. 这适用于ES5 +或.bind() polyfill。


Use an ES6 arrow function shortcut 使用ES6箭头功能快捷方式

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

This works in ES6+ 这适用于ES6 +


Personally, I prefer the .bind() implementation because I think it's just simpler and more declarative/clearer than any of the others and the ES6 "shortcut" isn't really shorter. 就个人而言,我更喜欢.bind()实现,因为我认为它比其他任何一个更简单,更具说明性/更清晰,而且ES6“快捷方式”并不是真正的缩短。

router.get() won't call your class the way you think it will. router.get()不会以你认为的方式调用你的类。 You are giving it a reference to a function which it will call in the context of the router.get meaning, it won't be in the context of your userController. 你给它一个函数的引用,它将在router.get的上下文中调用。这意味着它不会在你的userController的上下文中。

You can fix this by doing: 您可以通过以下方式解决此问题

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

In other words, don't have express use userController 's reference to list , have express use a closure that will have userController call list with the given arguments. 换句话说,没有明确使用userControllerlist的引用,已经明确使用了一个带有给定参数的userController调用list的闭包。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM