`expressJS路由处理程序中未定义this

`this` is undefined in expressJS route handler

本文关键字:未定义 this 程序 处理 expressJS 路由      更新时间:2023-09-26

groups.js

class groupsCtrl {
  constructor() {
    this.info = "test";
  }
  get(res, req) {
    console.log("LOG ! ", JSON.stringify(this));
  }
}
module.exports = new groupsCtrl(); //singleton

路由.js

var express = require('express');
var router = express.Router();
var groupsCtrl = require('controllers/api_admin/groups.js');
router.get('/groups/', groupsCtrl.get);

此日志记录LOG ! undefined

如何访问控制器类中的this

您需要将方法绑定到实例。

一种解决方案:

router.get('/groups/', groupsCtrl.get.bind(groupsCtrl));

另一种解决方案:

constructor() {
  this.info = "test";
  this.get  = this.get.bind(this);
}

或者使用类似es6bindall的东西(它基本上与上面的代码相同,但在需要绑定多个方法时可能更有用)。

class groupsCtrl {
    constructor() {
        this.info = 'test';
    }
    get = (res, req) => {
        console.log('LOG ! ', JSON.stringify(this));
    };
}

您可以使用箭头函数来避免样板代码

2020更新

  • 虽然@robertkiep发布的两个解决方案都没有得到这份工作,但我想强调的是,两者看起来都很难看,而且都不可维护
  • 当有大量路由时,执行router.get('/groups/',groupsCtrl.get.bind(groupsCtrl))的方法1看起来非常难看
  • 当控制器有许多路由时,方法2会变得麻烦
  • 由于你的例子只有一条路线,让我来说明这个问题

使用方法2

class AuthController {
  constructor({ db, pgp, logger }) {
    super({ db, pgp, logger })
    this.postLogin = this.postLogin.bind(this)
    this.postLogout = this.postLogout.bind(this)
    this.postSignup = this.postSignup.bind(this)
    this.postForgot = this.postForgot.bind(this)
    this.getReset = this.getReset.bind(this)
    this.postReset = this.postReset.bind(this)
  }
  postLogin(req, res, next) {
    
  }
  postLogout(req, res, next) {
  }
  async postSignup(req, res, next) {
    
  }
  async postForgot(req, res, next) {
    
  }
  async getReset(req, res, next) {
    
  }
  async postReset(req, res, next) {
    
  }
}

每次添加新方法时,构造函数都需要进一步更新

方法3

在我看来,这要干净得多,不需要维护,你可以继续添加你想要的方法

  • 其想法是使用Object.hasOwnPropertyName来获取所有方法名称的数组,然后以编程方式绑定它们
  • 例如,如果您编写Object.hasOwnPropertyName(AuthController.protype),它将为您提供数组中的所有非静态方法
  • 在上面的示例中,您将得到['constructor','postLogin','postLogout'…]
  • 如果调用Object.hasOwnPropertyName(AuthController),则会得到STATIC方法
  • 让我们以编程方式调用它们

这个控制器几乎不需要维护,只需要记住静态和非静态方法,通过过滤掉构造函数来删除它,然后在每个上调用绑定

class AuthController {
  constructor({ db, pgp, logger }) {
    super({ db, pgp, logger })
    this.postLogin = this.postLogin.bind(this)
    this.postLogout = this.postLogout.bind(this)
    this.postSignup = this.postSignup.bind(this)
    this.postForgot = this.postForgot.bind(this)
    this.getReset = this.getReset.bind(this)
    this.postReset = this.postReset.bind(this)
    Object.getOwnPropertyNames(AuthController.prototype)
      .filter((propertyName) => propertyName !== 'constructor')
      .forEach((method) => (this[method] = this[method].bind(this)))
  }
  postLogin(req, res, next) {
    
  }
  postLogout(req, res, next) {
  }
  async postSignup(req, res, next) {
    
  }
  async postForgot(req, res, next) {
    
  }
  async getReset(req, res, next) {
    
  }
  async postReset(req, res, next) {
    
  }
}

上面的答案很好,我想添加一点帮助澄清:

假设我们有一个类:

class TClass {
  constructor(arg) {
    this.arg = arg
  }
  test() {
    console.log(this.arg)
  }
}

这将不起作用:

const t = new TClass("test")
const method = t.test  // method is simply a reference without context
method() // 'this' is not defined

这将起作用:

const t = new TClass("test")
t.test() // log 'test'

原因就像上面的注释一样,对函数的引用没有上下文