web-dev-qa-db-ja.com

このクラスメソッドで「this」が定義されていないのはなぜですか?

私はインターネット全体のように見えるものを検索しようとしましたが、まだマイクロサービス用に作成しているJSクラスの問題にまだ悩まされています(まだ少し学習中です)。

したがって、インスタンス化されたオブジェクトでクラスメソッドを呼び出そうとします。私の知識と私の(誤ったと思います)ユニットテストによると、それは機能するはずです。

まあ、私が受け取るエラーから始めましょう:

    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)

(そしてもっとたくさん)。

コード呼び出しコード:

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

コントローラーの呼び出し元

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;
};

thisUserControllerで定義されていない理由についてはまったくわかりません。

どんな助けも大いに利用されるでしょう。

18
KarlGdawg

これを行うと:

_router.get('/users', userController.list);
_

ルーターに渡されるのは、_.list_メソッドへの参照です。 userControllerインスタンスが失われます。これはルーターに固有のものではありません。これは、Javascriptでの受け渡し方法の一般的なプロパティです。さらに理解するために、あなたが本質的にしていることはこれです:

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

また、JavaScriptのstrictモードで、上記のlist()を呼び出すなど、オブジェクト参照なしで通常の関数を呼び出すと、thisundefinedになります関数。それがあなたの例で起こっていることです。これを修正するには、インタプリタがthis値を適切に設定できるように、userController.list(...)のように適切なオブジェクト参照を使用してメソッドが呼び出されるようにする必要があります。

この問題を解決するには、いくつかの方法があります。

独自の関数ラッパーを作成する

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

これは、Javascriptのどのバージョンでも機能します。


.bind()を使用して、適切なオブジェクトで呼び出すラッパーを作成します

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

これはES5 +または.bind()ポリフィルで機能します。


ES6矢印関数のショートカットを使用

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

これはES6以降で機能します


個人的には、.bind()実装が他のどれよりも単純で宣言的/明確であり、ES6の「ショートカット」が実際には短くないため、私はそれを好みます。

50
jfriend00

router.get()は、あなたが思っているようにクラスを呼び出しません。 router.getの意味で呼び出す関数への参照を与えていますが、userControllerのコンテキストではありません。

これを修正するには、次のようにします。

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

言い換えると、userControllerへのlistの参照を明示的に使用せず、指定された引数でuserControllerを呼び出すlistを使用するクロージャを明示的に使用する。

3
zero298