'this' в обычной функции против жирной стрелки в ES6, проиллюстрировано на примере Mongoose - PullRequest
0 голосов
/ 17 июня 2019

В 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, свойства (простите за менее правильные слова) в толстых стрелочных функциях, как в обычных определениях функций?

Эти сообщения весьма полезны:
«Функции стрелок» и «Функции» эквивалентны / заменяемы?
Как работает ключевое слово this?

Но я все еще жду ответов на мои конкретные вопросы в конкретных случаях, прежде чем выяснить их.

1 Ответ

2 голосов
/ 17 июня 2019

Суть вашего недоразумения заключается в следующем:

, что в функциях жирной стрелки есть область непосредственного объекта

Неверно.Его контекст разрешен в области выполняемой в данный момент функции / среды.

Примеры:

// 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. В этомслучай, какова область этого в толстых стрелках?

Как я уже сказал. Scope и this являются двумя несвязанными понятиями.Концепция this - это объект / экземпляр context - то есть, когда вызывается метод, на какой объект действует метод.Понятие scope так же просто, как и то, что представляют собой глобальные переменные и какие переменные существуют только внутри конкретной функции, и может развиться в более сложные понятия, такие как замыкания .

Таким образом, поскольку scope всегда одно и то же, единственное отличие заключается в функциях со стрелками, его контекст (его this ) определяется областью действия.То есть когда функция объявляется, где она объявляется?В корне файла?Тогда он имеет глобальную область видимости и это равно "undefined".Внутри другая функция?Тогда это зависит от того, как эта функция вызывается.Если он был вызван как метод объекта, такого как UserSchema.methods, например, если UserSchema.methods.generatePasswordSetter() возвращает функцию стрелки, то эта функция (назовем ее setPassword()) будет иметь эту точку направильный объект.

Какова область этого в нормальных определениях функций?

Исходя из моего объяснения выше, я могу только сказать, что область действия не связана со значением this внормальные функции.Более подробное объяснение того, как работает this, смотрите в моем ответе на этот другой вопрос: Как ключевое слово "this" в Javascript действует внутри литерала объекта?

Как получить доступ к объекту, в этом случае: UserSchema, свойства (простите за менее правильные слова) в функциях жирной стрелки, как в обычных определениях функций?

Способэто определено, это невозможно.Вы должны определить его из обычной функции, которая имеет this, указывающую на UserSchema:

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

Но это, вероятно, не то, что вы хотите.Чтобы сделать то, что вы хотите, вам нужно всего лишь прекратить использование функций стрелок в этом случае.Регулярные функции все еще существуют для таких вариантов использования, как этот.

...