Home > Net >  undefined this in object function method express routing
undefined this in object function method express routing

Time:01-08

I'm new to express. for routing I create an object in a file and export the object

import userController from "../controllers/userController.js"

// get all users
router.get("/", isAuth, isAdmin, userController.list)

the problem is, this is undefined on userController

const userController = {
  _statusCode: 200,

  async list( req, res ) {
    console.log(this);
    try {
      const users = await User.find()
      return res.status( this._statusCode ).json( respSC( users, this._statusCode ) )
    } catch( err ) {
      this.statusCode = 500
      return res.status( this._statusCode ).json( respER( this._statusCode, err ) )
    }
  }
}

export default userController

OR

const userController = {
  _statusCode: 200,

  list: async function( req, res ) {
    console.log(this);
    try {
      const users = await User.find()
      return res.status( this._statusCode ).json( respSC( users, this._statusCode ) )
    } catch( err ) {
      this.statusCode = 500
      return res.status( this._statusCode ).json( respER( this._statusCode, err ) )
    }
  }
}

export default userController

OR

const userController = {
  _statusCode: 200,

  list: async ( req, res ) => {
    console.log(this);
    try {
      const users = await User.find()
      return res.status( this._statusCode ).json( respSC( users, this._statusCode ) )
    } catch( err ) {
      this.statusCode = 500
      return res.status( this._statusCode ).json( respER( this._statusCode, err ) )
    }
  }
}

export default userController

in all three ways, the log is :

undefined
file:///C:/NodeJS/<project-folder>/app/controllers/userController.js:18
      return res.status( this._statusCode ).json( respER( this._statusCode, err ) )
                              ^

TypeError: Cannot read properties of undefined (reading '_statusCode')
    at list (file:///C:/NodeJS/<project-folder>/app/controllers/userController.js:18:31)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

I know about objects and I try create object in chrome console and this is defined, but in routing of express, this is undefined.

can anybody explain why it's happen?

CodePudding user response:

First, when you pass userController.list in this:

router.get("/", isAuth, isAdmin, userController.list)

The JS interpreter reaches into the userController object and gets a reference to the list method and what it passes is just a reference to that method all by itself and will have no connection at all to the userController object at all. Thus, the list() method will just be called as a regular function by Express (because it doesn't even know about the userController object and (if operating in strict mode), then calling a regular function leaves the this value as undefined.

So, that explains why what you observe is happening.

Now, how to fix that. The simplest way would be use .bind():

router.get("/", isAuth, isAdmin, userController.list.bind(userController));

This passes a dynamically generated stub function to Express and when Express calls that stub function and the stub function then calls your list method in the right way to set the this value inside it to userController.

There are other ways to do this too:

  • You could change the way the methods are declared so they will automatically bind to their parent object.
  • You could write your own sub function that calls list as userController.list() and you pass your own stub function to Express.
  • You can use utility functions in your class that auto-bind methods.
  • There's even a new ECMAScript syntax proposal for a binding operator with ::.

Here's a simple code example that shows both the undefined you're seeing and the solution. The first example fn1 is what your current code is doing. The second example fn2 is my suggested solution.

"use strict";

class MyObj {
    constructor() {
        this.greeting = "Hi";
    }
    getGreeting() { console.log(this); }
};

const obj = new MyObj();

// get a non-bound method and call it
const fn1 = obj.getGreeting;
fn1();

// get a bound method and call it
const fn2 = obj.getGreeting.bind(obj);
fn2();

  • Related