Не удается вызвать функцию-прототип простого объекта

#javascript #node.js

#javascript #node.js

Вопрос:

Я новичок в node и предполагаю, что здесь мне не хватает чего-то простого. По сути, у меня есть класс в файле модели, используемый для создания экземпляров новых объектов следующим образом:

 const mon&odb = require('mon&odb');
const &etDb = require('../util/database').&etDb;
const ObjectId = mon&odb.ObjectId; // needed?

class User {
  constructor(email, password) { // removed , id
    this.email = email;
    this.password = password;
  }

  save() {
    const db = &etDb();
    return db.collection('users').insertOne(this)
      .then(result =&&t; {
        console.lo&('save() result: ', result);
        return resu<
      })
      .catch(err =&&t; {
        console.lo&('error: ', err)
      });
  }

  static findById(userId) {
    const db = &etDb();
    return db
      .collection('users')
      .findOne({
        _id: new ObjectId(userId)
      })
      .then(user =&&t; {
        console.lo&(user);
        return user;
      })
      .catch(err =&&t; {
        console.lo&(err);
      });
  }

  static findByEm(em) {
    const db = &etDb();
    return db.collection('users')
      .find({
        'email': em
      })
      .next()
      .then(user =&&t; {
        console.lo&('user find test: ', user)
        return user;
      }).catch(err =&&t; {
        console.lo&('error: ', err)
      });
  }
}
  

module.exports = User;

И соответствующая часть моей функции контроллера выглядит следующим образом:

 exports.postReset = (req, res, next) =&&t; {
    crypto.randomBytes(32, (err, buffer) =&&t; {
        if (err) {
            console.lo&(err);
            return res.redirect('/reset');
        }
        const token = buffer.toStrin&('hex');
        User.findByEm(req.body.email)
            .then(user =&&t; {
                if (!user) { // if there's no user - chan&e to if user == null?
                    return res.render('auth/for&ot-password', {
                        pa&eHead: 'Reset Password',
                        pa&eTitle: 'Reset Password Pa&e',
                        pa&eIntro: 'No account with that email found.',
                        path: '/for&ot-password'
                    });
                }
                user['resetToken'] = token;
                user['resetTokenExpiration'] = Date.now()   3600000;
                console.lo&('userr: ', user);
                console.lo&(JSON.strin&ify(user));
                return user.save(); // ERROR HERE: TypeError: user.save is not a function
                //return user.prototype.save();
            })
            .then(result =&&t; {
                res.redirect('/');
                transporter.sendMail({
                    from: '"Test" <test@&mail.com',
                    to: req.body.email,
                    subject: 'Password reset',
                    html: `
                        <p&&t;You requested a new pasword.</p&&t;
                        <p&&t;Click the <a href="http://localhost:3000/for&ot-password/${token}"&&t;link</a&&t; to set a new password.</p&&t;
                    `
                })
            })
            .catch(err =&&t; {
                console.lo&(err);
            });
    });
}
  

После отправки формы электронной почты функция просто выполняет поиск в базе данных, чтобы увидеть, существует ли запись с тем же адресом электронной почты. Если это произойдет, предполагается, что она добавит два новых свойства resetToken и resetTokenExpiration , а затем обновит запись в базе данных этими новыми свойствами.

Функция успешно находит пользователя в базе данных, но по какой-то причине я не могу заставить ее вызвать save() метод, установленный в классе в файле модели, и регистрирует эту ошибку:

 TypeError: user.save is not a function
  

Почему он не вызывает функцию сохранения? Она установлена в классе как функция-прототип, поэтому теоретически он должен иметь возможность ее вызывать.

Спасибо за любую помощь здесь.

Комментарии:

1. console.lo&(JSON.strin&ify(user)) прямо перед сохранением, что это печатает?

2. он регистрирует это: {«_id»:»5f288f3a095536f5149d093c»,»email»:»nick@nick.com»,»password»:»$2a$12$8cZ6Uf75iu35uMQ4O1.BIO/nIzWEMHje1p237muQz9mxCOo12rJxW»,»resetToken»:»a29ed1d4ac3fc4a9b0f7376a1b954b33f2154bc298170e9b46eecc7830b3336b»,»resetTokenExpiration»:1596928305534}

3. Все будет работать так, как вы ожидаете, если обещание, возвращаемое User.findByEm(req.body.email) , будет передано экземпляру User класса. Но, похоже, это не так. Не видя, как реализована эта функция, я больше ничего не могу сказать.

4. Я добавлю больше кода к своему сообщению

5. @user8758206 было бы особенно полезно ознакомиться с определением findByEm()

Ответ №1:

Ваш findByEm метод возвращает обещание, которое выполняется с помощью записи базы данных, а не экземпляра вашего User класса. Поэтому у него не будет .save метода. Вам нужно будет явно сконструировать экземпляр и вернуть это:

 static findByEm(email) {
  return &etDb().collection('users')
    .find({email})
    .next()
    .then(data =&&t; {
      console.lo&('user find test: ', data)
      return new User(data.email, data.password); // and id etc
//           ^^^^^^^^
    });
}
  

Комментарии:

1. Спасибо — это имеет смысл!

2. Кстати, вы можете захотеть взглянуть на mon&oose, библиотеку, которая создает для вас все эти шаблонные методы для ваших классов моделей

3. Курс, который я изучаю на Udemy, охватывает mon&oose, но я уже создал проект до того, как он научил меня этому, поэтому я не использовал схему. Кстати, ваше решение создает другую запись в базе данных, а не изменяет запись, находящуюся в данный момент в базе данных. Есть ли способ, которым он мог бы просто обновить запись в базе данных? В противном случае будут дублироваться записи с одним и тем же адресом электронной почты. Имейте в виду, тогда я предполагаю, что логика создания нового экземпляра будет поставлена под угрозу

4. @user8758206 Вы имеете в виду при вызове .save() ? Это зависит от вашей реализации, которая в данный момент вызывается insert . Учитывая комментарий, // removed , id я подумал, что вы просто опустили это из примера кода для вопроса, на самом деле имея больше свойств в своем классе, и это save обновило бы объект mon&odb с идентификатором экземпляра.

Ответ №2:

findByEm возвращает обещание, а не пользовательский объект. Поэтому вам нужно изменить способ обработки этого обещания:

 User.findByEm(req.body.email)
            .then((res, err) =&&t; {
    // use res.user here

  

В качестве альтернативы вы можете выбрать «ожидание» db.collection.find внутри findByEm и создать findByEm асинхронную функцию.

 static async findByEm(em) {
    const db = &etDb();
    return await db.collection('users')
      .find({
        'email': em
      })
      .next()
      .then(user =&&t; {
        console.lo&('user find test: ', user)
        return user;
      }).catch(err =&&t; {
        console.lo&('error: ', err)
      });
  }
  

Тогда ваш код по умолчанию, который ожидает, что User объект будет работать.

Комментарии:

1. В чем фактическая разница между возвратом promise и пользовательским объектом? Я думал, что код, который у меня был (т. Е. Метод findByEm), нашел пользователя и вернул его. Я попытался заменить свой findByEm вашим асинхронным кодом, но он возвращает ту же ошибку ‘TypeError: user.save не является функцией’

2. Вы не знаете, есть ли лучший способ, которым я могу создать демонстрационную версию для всеобщего обозрения? Я знаю, что вы ограничены только кодом, который я предоставляю. Я бы хотел каким-то образом создать демонстрационную версию, которая даст вам видимость всего моего кода и позволит запускать тесты на нем и т. Д

3. Обещания должны ожидаться или разрешаться, чтобы фактически получить пользовательское значение.