web-dev-qa-db-ja.com

Nodejs、es6クラスとしてルートを表現

プロジェクトを少し片付けたいので、今度はルートにes6クラスを使用しようとしています。私の問題はthisが常に未定義であることです。

var express = require('express');
var app = express();

class Routes {
    constructor(){
        this.foo = 10
    }

    Root(req, res, next){
        res.json({foo: this.foo}); // TypeError: Cannot read property 'foo' of undefined
    }
}

var routes = new Routes();
app.get('/', routes.Root);
app.listen(8080);
22
Michael Malura
import express from 'express';
const app = express();

class Routes {
    constructor(){
        this.foo = 10
    }

    const Root = (req, res, next) => {
        res.json({foo: this.foo}); // TypeError: Cannot read property 'foo' of undefined
    }
}

const routes = new Routes();
app.get('/', routes.Root);
app.listen(8080);

これはコードのマイナーな書き直しですが、ここでいくつかの回答が指摘したように、ルート構成でそのように参照されたときの関数自体はthisを認識せず、バインドする必要があります。これを回避するには、「通常の」関数を作成する代わりに、自分自身を自動的にバインドする定義「太い矢印」関数を作成するだけでよいのです。

1
Balkana

コードを使用してthisを固定しようとします:

app.get('/', routes.Root.bind(routes));

下線 bindAll 関数を使用して、ボイラープレートから抜け出すことができます。例えば:

var _ = require('underscore');

// ..

var routes = new Routes();
_.bindAll(routes, 'Root')
app.get('/', routes.Root);

Es7を使用すると、よりエレガントな方法でコードを記述できることもわかりました。

class Routes {
    constructor(){
        this.foo = 10
    }

    Root = (req, res, next) => {
        res.json({foo: this.foo});
    }
}

var routes = new Routes();
app.get('/', routes.Root);
15
zag2art

これは、表現するスタンドアロン関数としてメソッドを渡したために発生しています。 Expressは、それが由来するクラスについて何も知らないため、メソッドが呼び出されたときにthisとして使用する値がわかりません。

thisの値はbindで強制できます。

app.get('/', routes.Root.bind(routes));

または、ルートを管理するための代替構成を使用できます。クラスがなくても、オブジェクト指向プログラミングの多くの構文上の利点を利用できます。

function Routes() {
  const foo = 10;

  return {
    Root(req, res, next) {
      res.json({ foo });
    }
  };
}

const routes = Routes();
app.get('/', routes.Root);
app.listen(8080);
  • thisの値を気にする必要はありません。
  • 関数がnewで呼び出されるかどうかは関係ありません
  • 各ルートでbindを呼び出す複雑さを回避できます

リソースの良いリストがあります ここ 、なぜES6クラスが見た目ほど良くないのかについてです。

10
Dan Prince

または、ルートごとにコンテキストをバインドしたくない場合は、オプションでクラスのコンストラクタ自体のメソッドにコンテキストをバインドできます。

例えば:

constructor() {
   this.foo = 10;
   this.Root = this.Root.bind(this);
}
3

最近、すべてのExpressコントローラーをリファクタリングし、基本コントローラークラスを使用しましたが、この問題も発生しました。私たちの解決策は、コンストラクターから次のヘルパーメソッドを呼び出すことによって、各コントローラーがそのメソッドをそれ自体にバインドすることでした。

  /**
   * Bind methods
   */
  bindMethods() {

    //Get methods
    const proto = Object.getPrototypeOf(this);
    const methods = [
      ...Object.getOwnPropertyNames(Controller.prototype),
      ...Object.getOwnPropertyNames(proto),
    ];

    //Bind methods
    for (const method of methods) {
      if (typeof this[method] === 'function') {
        this[method] = this[method].bind(this);
      }
    }
  }

これにより、両方の親コントローラーメソッドand子コントローラークラスのカスタムメソッドが正しくバインドされます(例:Foo extends Controller)。

1
Adam Reis

上記の答えは少し複雑に思えます。ここで私がやったことをチェックしてください:

class Routes {
  constructor(req, res, next) {
    this.req = req;
    this.res = res;
    this.next = next;
    this.foo = "BAR"
    // Add more data to this. here if you like
  }

  findAll (){
    const {data, res,} = this; // Or just reference the objects directly with 'this'
    // Call functions, do whaterver here...
    // Once you have the right data you can use the res obejct to pass it back down

    res.json ({foo: this.foo}); // Grabs the foo value from the constructor

  }
}

このクラスを使用することになると、これに沿って何かを行うことができます:

var express = require('express');
var router = express.Router();
var {Routes} = require('./Routes');

router.get('/foo', (req, res, next) => {
  new Routes(req, res, next).findAll();
});

私は2つのファイルを分離するので、RoutesファイルにRouterクラスを必要とするだけです。

これが役に立てば幸い!

1
James111