Nodejs,将路由表示为 es6 类

Nodejs, express routes as es6 classes

我想稍微清理一下我的项目,现在我尝试使用 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);

尝试使用代码固定 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 对它来自的 class 一无所知,因此它不知道在调用您的方法时将哪个值用作 this

您可以将 this 的值强制设置为 bind

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

或者您可以使用另一种构造来管理路由。在没有 classes.

的情况下,您仍然可以利用面向对象编程的许多语法优势
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 的复杂性

有一个很好的资源列表 here,说明为什么 ES6 classes 不像它们看起来那么好。

以上回答似乎有点复杂。看看我在这里做了什么:

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

  }
}

现在,在使用这个 class 时,您可以按照以下方式做一些事情:

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

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

我会将这两个文件分开,这样您只需将 Routes class 放入您的 Router 文件即可。

希望对您有所帮助!

或者,如果您不喜欢为每个路由绑定上下文,您可以选择将其绑定到 class' 构造函数本身中的方法。

例如:

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

我们最近重构了所有 Express 控制器以使用基本控制器 class,并且 运行 也解决了这个问题。我们的解决方案是通过从构造函数调用以下辅助方法,让每个控制器将其方法绑定到自身:

  /**
   * 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);
      }
    }
  }

这确保父控制器方法 子控制器 class 中的任何自定义方法都正确绑定(例如 Foo extends Controller)。

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 并且必须被绑定。要解决这个问题,您无需编写 "normal" 函数,只需编写定义 "fat arrow" 自动绑定自身的函数,您就可以开始了!