プロジェクトを少し片付けたいので、今度はルートに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);
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
を認識せず、バインドする必要があります。これを回避するには、「通常の」関数を作成する代わりに、自分自身を自動的にバインドする定義「太い矢印」関数を作成するだけでよいのです。
コードを使用して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);
これは、表現するスタンドアロン関数としてメソッドを渡したために発生しています。 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クラスが見た目ほど良くないのかについてです。
または、ルートごとにコンテキストをバインドしたくない場合は、オプションでクラスのコンストラクタ自体のメソッドにコンテキストをバインドできます。
例えば:
constructor() {
this.foo = 10;
this.Root = this.Root.bind(this);
}
最近、すべての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
)。
上記の答えは少し複雑に思えます。ここで私がやったことをチェックしてください:
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
クラスを必要とするだけです。
これが役に立てば幸い!