Mongoose的pre save使用了错误的“this”上下文?

4

有人能帮我找出下面代码的问题吗?

从文档来看,Mongoose的.pre('save')方法中的this应该是模型本身,但在我的代码中,this最终变成了一个空对象。

const Mongoose = require('./lib/database').Mongoose;
const Bcrypt = require('bcrypt');

const userSchema = new Mongoose.Schema({
    email: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true }
});

userSchema.pre('save', (next) => {

    const user = this;

    Bcrypt.genSalt((err, salt) => {

        if (err) {
            return next(err);
        }

        Bcrypt.hash(user.password, salt, (err, encrypted) => {

            if (err) {
                return next(err);
            }

            user.password = encrypted;
            next();
        });
    });
});

const User = Mongoose.model('User', userSchema);

保存用户时,我遇到了以下错误:[错误:需要数据和salt参数]
function createUser(email, password, next) {

    const user = new User({
        email: email,
        password: password
    });

    user.save((err) => {

        if (err) {
            return next(err);
        }

        return next(null, user);
    });
}

createUser('test@email.com', 'testpassword', (err, user) => {

    if (err) {
        console.log(err);
    }
    else {
        console.log(user);
    }

    process.exit();
});

如果我移除.pre('save'),那么它当然可以正常保存了。我使用的Mongoose版本是4.2.6。


首先,为什么要使用 const?请改用 var,然后在预处理脚本中使用 this 指代记录而不是模型。 - michelem
我使用const,因为这些变量从不改变。 我尝试更改为var,代码运行相同。 - jawang35
3个回答

7

问题在这里是箭头函数。您必须使用普通函数重写回调函数。以下是一个小例子,以展示差异

var obj = {};

obj.func1 = function () {
    console.log(this === obj);
};

obj.func2 = () => {
    console.log(this === obj);
};

obj.func1(); // true
obj.func1.bind(obj)(); // true

obj.func2(); // false
obj.func2.bind(obj)(); // false

谢谢。我刚刚想到了这个并正在撰写自己的答案,就在您发布这篇文章之前。 - jawang35
它们只是“箭头函数”,不是“胖箭头函数”。 - loganfsmyth
箭头函数表达式(也称为“胖箭头函数”)(с)MDN - Alexey B.
谢谢!千万次感谢!我似乎按照每个指示都做了,但还是无法弄清楚发生了什么......作为一个几乎不懂JS的新手,箭头函数和普通函数之间的区别还没有深入我的脑海。这个问题现在肯定解决了!再次感谢。 - Gormador

5

我能够找出这个问题的原因。原来ES6中的箭头函数会保留声明作用域的上下文,而不是使用调用作用域的上下文,所以将代码更改为以下内容就可以解决问题。

userSchema.pre('save', function (next) {

    Bcrypt.genSalt((err, salt) => {

        if (err) {
            return next(err);
        }

        Bcrypt.hash(this.password, salt, (err, encrypted) => {

            if (err) {
                return next(err);
            }

            this.password = encrypted;
            next();
        });
    });
});

感谢Michelem给了我一个想法,可能是ES6引起了问题。


同时,由于箭头函数从声明范围捕获'this',因此不再需要声明'user'变量。 - jawang35
箭头函数导致了我的问题,感谢指出上下文/作用域提示! - electblake

0

我认为应该是:

userSchema.pre('save', (next) => {

    var user = this;

    // Try this
    // console.log(user);

    Bcrypt.genSalt(function (err, salt) {

        if (err) {
            return next(err);
        }

        Bcrypt.hash(user.password, salt, null, function (err, encrypted) {

            if (err) {
                return next(err);
            }

            user.password = encrypted;
            next();
        });
    });
});

但请确保 user 是正确的对象。


还是不行。除了将 const 改为 var,并将语法改回 ES5,这段代码与我的有什么不同? - jawang35

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接