在原型方法中修改内部函数的实例变量

时间:2012-04-14 01:21:29

标签: javascript oop scope

我正在尝试将数据库中的数据建模为服务器端JavaScript(Node)中的“类”。 目前,我只是在适当的情况下使用传统的构造函数模式和原型方法,并将构造函数公开为整个module.exports。这是服务器端的事实对于问题的核心并不重要,但我认为我会将其作为参考提供。

代码的问题区域如下所示:

User.prototype.populate = function() {  
    var that = this;    
    db.collection("Users").findOne({email : that.email}, function(err, doc){
        if(!err) {
            that.firstName  = doc.firstName;
            that.lastName   = doc.lastName;
            that.password   = doc.password;
        }
        console.log(that); //expected result
    });
   console.log(that); //maintains initial values
};

每当我调用此函数时,findOne()完成后对对象的更改不会保留。我意识到this的范围用新的函数作用域改变了全局对象,因此我将其引用保持为that。如果来自匿名函数中的console.log(that),则数据会按预期显示在其属性中。但是,如果我在函数完成后记录that,它将保持函数开头的状态。

这里发生了什么以及如何按预期更改实例变量?

1 个答案:

答案 0 :(得分:2)

  

"但是,如果我在功能完成后记录,..."

由此我假设你正在做这样的事......

var user = new User

user.populate();

console.log(user);

如果是这样,console.log将在调用异步回调到.findOne()之前很久就会运行。

需要在回调中调用任何依赖于findOne响应的代码。


编辑: 您的更新与我上面的示例略有不同,但原因是相同的。

将回调传递给findOne方法的全部原因是它执行异步活动。如果它没有,则没有理由进行回调。您只需在调用findOne后直接放置内部代码,就像使用console.log()一样。

但是因为它是异步,后续代码不会等待执行。这就是您在控制台中获取无人居住对象的原因。

如果您为每个console.log()添加标签,则会发现它们无序执行。


var that = this;    
db.collection("Users").findOne({email : that.email}, function(err, doc){
    if(!err) {
        that.firstName  = doc.firstName;
        that.lastName   = doc.lastName;
        that.password   = doc.password;
    }
    console.log("inside the callback", that); // this happens Last!!!
});

console.log("outside the callback", that); // this happens First!!!

所以一旦你观察到console.log次调用的顺序,那么在回调中的那个之前发生空的那个顺序就会变得清晰。


编辑 您还可以让.populate()方法接收在.findOne回调中调用的回调。

User.prototype.createNickName = function () {
    this.nickname = this.firstName.slice(0,3) + '_' + this.lastName.slice(0,3);
};

    // >>>------------------------------v----receive a function argument...
User.prototype.populate = function(callback_func) {  
    var that = this;    
    db.collection("Users").findOne({email : that.email}, function(err, doc){
        if(!err) {
            that.firstName  = doc.firstName;
            that.lastName   = doc.lastName;
            that.password   = doc.password;
        }

          // all users will have the "createNickName()" method invoked
        that.createNickName();

          // ...and invoke it in the callback.
        callback_func.call(that);

          // By using .call(), I'm setting the "this" value
          //    of callback_func to whatever is passed as the first argument
    });
};

   // this user wants to log the firstName property in all caps
var user1 = new User;

user1.populate(function() {
    console.log(this.firstName.toUpperCase());
});


   // this user wants to log the the whole name
var user2 = new User;

user2.populate(function() {
    console.log(this.firstName + ' ' + this.lastName);
});


   // this user wants to verify that the password is sufficiently secure
var user3 = new User;

user3.populate(function() {
    console.log(verify_password(this.password));
});