【问题标题】:'this' in regular function vs fat arrow function in ES6, illustrated with an example of Mongoose常规函数中的“this”与 ES6 中的胖箭头函数,以 Mongoose 为例进行说明
【发布时间】:2019-10-30 15:03:02
【问题描述】:

router/user.js 中进行路由:

router.post('/register', auth.optional, (req, res, next) => {
  const {
    body: { user }
  } = req

  // validation code skipped for brevity

  const finalUser = new User(user)
  finalUser.setPassword(user.password)

  // to save the user in Mongo DB
  return finalUser.save().then(() => res.json({ user: finalUser.toAuthJSON() }))
})

发送的请求正文为:

{
    "user":{
        "email":"leon@idiot.com",
        "password": "123abc"
    }
}

model/User.js 中用于数据库架构:

const UserSchema = new Schema({
  email: String,
  hash: String,
  salt: String
})


// Please note: this is a regular/normal function definition
UserSchema.methods.setPassword = function (password) {
  // this references the UserSchema created
  this.salt = crypto.randomBytes(16).toString('hex')
  console.log(`salt: ${this.salt}`)
  this.hash = crypto
    .pbkdf2Sync(password, this.salt, 10000, 512, 'sha512')
    .toString('hex')
  console.log(`hash: ${this.hash}`)
}

现在一切正常。日志输出为:

salt: e7e3151de63fc8a90e3621de4db0f72e
hash: 19dd9fdbc78d0baf20513b3086976208ab0f9eee6d68f3c71c72cd123a06459653c24c11148db03772606c40ba4846e2f9c6d4f1014d329f01d22805fc988f6164fc13d4157394b118d921b9cbd742ab510e4d2fd4ed214a0d523262ae2b2f80f6344fbd948e8c858f95ed9706952db90d415312156a994c65c42921afc8c3e5b1b24a923219445eec8ed62de313ab3d78dc93b715689a552b6449870c5bfcc3bec80c4438b1895cab41f92ef681344ac8578de476a82aa798730cf3a6ef86973a4364a8712c6b3d53ce67ffffd7569b9ade5db09ad95490354c6f7194fdd9d8f8a1cb7ccddf59e701198a1beee59a2dd6afb90ae50e26ea480e9a6d607e4b37857a02016ee4d692d468dd9a67499547eb03fc6cfa676686f7990c2251c9516459288c55584138aed56a5df6c4692f7ef6925e8f3d6f6a0c780c4d80580447f2b1258bea799a8c7eb9da878ab70a94c4227ec03d18d56b2722c315d0e2b2681d81d78d4213288f7305cbbfa377c3b2eb75e0f0b093e6067b14adce4a01f0a7bde8515350a1c987739c12574ec4c49008510e2e7e5534f9b76d15b1af68e43ef54e6b8a1bea859aafd23d6b6bc61d5b1965004cd6dd933545cf755f3e6dfc8f230f37a79a8bc006b9b14465b1b08d60cb45ef3b6a1b73f5afac90bdc58d5ec15c7596dc7e8d503f8dfbd6a3289cf997da2031389c7f3d165e34b29178f3daf76d
3

但它不适用于这样的定义:

UserSchema.methods.setPassword = password => {
  // what this reference is undefined, so are ones below
  this.salt = crypto.randomBytes(16).toString('hex')
  console.log(`salt: ${this.salt}`)
  this.hash = crypto
    .pbkdf2Sync(password, this.salt, 10000, 512, 'sha512')
    .toString('hex')
  console.log(`hash: ${this.hash}`)
}

错误是:

{
    "errors": {
        "message": "Cannot set property 'salt' of undefined",
        "error": {}
    }
}

这意味着 this 引用的内容是未定义的。

我在网上找到的是胖箭头函数明确阻止this 的绑定,这是范围的问题,胖箭头函数中的this 具有其直接对象的范围。但我不能说我非常了解它。
1、在这种情况下,胖箭头函数中this的作用域是什么?
2. 在普通函数定义中this 的范围是什么?
3. 如何访问对象,在这种情况下:UserSchema,胖箭头函数中的属性(请原谅我不太恰当的词)就像在普通函数定义中一样?

这些帖子很有帮助:
Are 'Arrow Functions' and 'Functions' equivalent / exchangeable?
How does the “this” keyword work?

但在弄清楚之前,我仍然期待我在特定情况下的具体问题得到答案。

【问题讨论】:

标签: node.js mongoose ecmascript-6 arrow-functions


【解决方案1】:

你误解的核心是这样的:

胖箭头函数中的 this 有一个直接对象的作用域

错了。它的上下文在当前执行的函数/环境的范围内解析。

例子:

// global scope outside of any function:

let foo = {};

// Define a method in global scope (outside of any function)
foo.a = () => {
    console.log(this); // undefined
}

// Return a function from scope of a method:
foo.b = function () {
    // remember, "this" in here is "foo"

    return () => {
        console.log(this); // foo - because we are in scope foo.c()
    }
}

foo.a();   // undefined
foo.b()(); // foo

对于箭头函数,重要的不是函数所属的对象,而是定义的位置。在第二个例子中,函数可以完全不属于foo,但仍然会打印foo

bar = {};
bar.b = foo.b();

bar.b(); // will log "foo" instead of "bar"

这与常规函数相反,常规函数取决于您如何调用它们而不是定义它们的位置:

// Defined in global scope:
function c () {
    console.log(this);
}

bar.c = c;
bar.c(); // will log "bar" instead of undefined because of how you call it

注意

请注意,这里有两个非常不同的概念混合在一起 - context(“this”具有什么值)和 scope(在函数中可以看到哪些变量) .箭头函数使用范围来解析上下文。常规函数不使用作用域,而是取决于您如何调用它们。

问题

现在回答你的一些问题:

  1. 在这种情况下,胖箭头函数中 this 的范围是什么?

正如我所说。 Scopethis 是两个不相关的概念。 this 背后的概念是对象/实例 context - 也就是说,当调用方法时,该方法作用于哪个对象。 作用域的概念就像什么是全局变量以及什么变量只存在于特定函数中一样简单,它可以演变成更复杂的概念,如闭包

所以,由于 scope 总是相同的,唯一的区别在于箭头函数,它的上下文(它的 this)由范围定义。也就是在声明函数的时候,在哪里声明呢?在文件的根目录?然后它具有全局范围并且 this 等于 "undefined"。在另一个函数里面?然后取决于如何调用该函数。如果它作为UserSchema.methods 之类的对象的方法被调用,例如如果UserSchema.methods.generatePasswordSetter() 返回一个箭头函数,那么该函数(我们称之为setPassword())将有它的 this 指向正确的对象。

  1. 正常函数定义中 this 的范围是什么?

根据我上面的解释,我只能认为范围与正常函数中的 this 的值无关。有关this 工作原理的更详细说明,请参阅我对另一个问题的回答:How does the "this" keyword in Javascript act within an object literal?

  1. 如何访问对象,在这种情况下:UserSchema,属性(请原谅我不恰当的词)在粗箭头函数中的作用与普通函数定义中的一样?

它的定义方式是不可能的。您需要从一个常规函数中定义它,该函数的 this 指向 UserSchema

UserSchema.methods.generatePasswordSetter = function () {
    return (password) => { /* implementation... */}
}

但这可能不是你想要的。在这种情况下,要做你想做的事,你只需要停止使用箭头函数。诸如此类的用例仍然存在常规函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-01
    • 2019-02-19
    • 2015-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多