在对象方法表达式路由中未定义的 “this”。

huangapple go评论78阅读模式
英文:

undefined this in object function method express routing

问题

我是新手使用Express。
在路由中,我创建了一个对象并将其导出。

// 导入用户控制器
import userController from "../controllers/userController.js"

// 获取所有用户
router.get("/", isAuth, isAdmin, userController.list)

问题是,userController 中的 this 未定义。

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

或者

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

或者

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

在这三种方式中,日志输出都是:

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)

我知道关于对象,我尝试在Chrome控制台中创建对象,它被定义了,但在Express路由中,它却未定义。

有人能解释为什么会发生这种情况吗?


<details>
<summary>英文:</summary>

I&#39;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&#39;s happen?

</details>


# 答案1
**得分**: 1

首先,在这里传递`userController.list`时:

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

JS 解释器会访问 userController 对象并获取到 list 方法的引用,所传递的仅仅是该方法的引用本身,与 userController 对象没有任何关联。因此,list() 方法将被 Express 作为普通函数调用(因为它甚至不知道 userController 对象的存在),并且(如果在 strict 模式下运行),则调用普通函数会将 this 值设置为 undefined

因此,这解释了为什么出现了你所观察到的情况。

那么,如何修复呢。 最简单的方法是使用 .bind()

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

这会将一个动态生成的存根函数传递给 Express,当 Express 调用该存根函数时,存根函数将以正确的方式调用你的 list 方法,以将 this 值设置为 userController

还有其他方法可以做到这一点:

  • 你可以更改方法声明的方式,使它们自动绑定到其父对象。
  • 你可以编写自己的子函数,以 userController.list() 的方式调用 list,然后将自己的存根函数传递给 Express。
  • 你可以在类中使用实用函数自动绑定方法。
  • 甚至有一个新的 ECMAScript 语法提案,使用 :: 绑定运算符。

以下是一个简单的代码示例,展示了你所看到的 undefined 以及解决方案。第一个示例 fn1 是你当前的代码所做的事情。第二个示例 fn2 是我建议的解决方案。

<!-- 开始代码示例: js 隐藏: false 控制台: true babel: false -->

<!-- 语言: lang-js -->

"use strict";

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

const obj = new MyObj();

// 获取一个未绑定的方法并调用它
const fn1 = obj.getGreeting;
fn1();

// 获取一个绑定的方法并调用它
const fn2 = obj.getGreeting.bind(obj);
fn2();

<!-- 结束代码示例 -->

希望这有所帮助。

英文:

First, when you pass userController.list in this:

router.get(&quot;/&quot;, 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(&quot;/&quot;, 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.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

&quot;use strict&quot;;

class MyObj {
    constructor() {
        this.greeting = &quot;Hi&quot;;
    }
    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();

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年1月8日 14:26:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75045887.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定