最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Nodejs, express routes as es6 classes - Stack Overflow

programmeradmin11浏览0评论

I want to clean up my project a bit and now i try to use es6 classes for my routes. My problem is that this is always undefined.

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

I want to clean up my project a bit and now i try to use es6 classes for my routes. My problem is that this is always undefined.

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);
Share Improve this question asked Nov 19, 2015 at 8:44 Michael MaluraMichael Malura 1,1812 gold badges13 silver badges31 bronze badges
Add a comment  | 

6 Answers 6

Reset to default 20

try to use the code to pin this:

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

You can get out of the boilerplate using underscore bindAll function. For example:

var _ = require('underscore');

// ..

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

I also found that es7 allows you to write the code in a more elegant way:

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

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

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

This is happening because you've passed a method as a standalone function to express. Express doesn't know anything about the class that it comes from, therefore it doesn't know which value to use as this when your method is called.

You can force the value of this with bind.

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

Or you can use an alternative construct for managing routes. You can still make use of a lot of the syntactic benefits for object oriented programming without classes.

function Routes() {
  const foo = 10;

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

const routes = Routes();
app.get('/', routes.Root);
app.listen(8080);
  • You won't have to worry about the value of this
  • It doesn't matter whether the function is called with new or not
  • You can avoid the complexity of calling bind on each route

There's a good list of resources here, on why ES6 classes are not as good as they might seem.

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

Here is minor rewrite of the code, but as some of the answers here pointed out, the function itself when referenced like that in the route config is unaware of this and has to be binded. To get around this, instead of writing "normal" functions, you just have to write define "fat arrow" functions that automatically bind themselves and you are good to go!

Or if you don't like binding the context per routes, you can optionally bind it to methods in your class' constructor itself.

E.g:

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

We recently refactored all our Express controllers to use a base controller class, and also ran into this issue. Our solution was to have each controller bind its methods to itself by calling the following helper method from the constructor:

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

This ensures that both the parent Controller methods and any custom methods in the child controller class are bound correctly (e.g Foo extends Controller).

The above answers seem a bit over complicated. Checkout what I've done here:

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

  }
}

Now when it comes to using this class you can do something along the lines of this:

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

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

I would seperate the two files, so that you just require the Routes class into your Router file.

Hope this helped!

发布评论

评论列表(0)

  1. 暂无评论