【问题标题】:Modifying instance variables from inner function in a prototype method在原型方法中从内部函数修改实例变量
【发布时间】:2012-04-26 08:54:24
【问题描述】:

我正在尝试将数据库中的数据建模为服务器端 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,它会保持函数开始时的状态。

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

【问题讨论】:

    标签: javascript oop scope


    【解决方案1】:

    “但是,如果我在函数完成后记录,...”

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

    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));
    });
    

    【讨论】:

    • 没错。虽然这发生在外部调用记录 user.populate() 以及 populate() 方法本身(但在 findOne() 范围之外)。我更新了帖子以反映我的意思。
    • @user574930:刷新您的浏览器。我已经更新了我的答案。
    • 有了这些知识,那么,我将如何实现我正在寻找的效果,我可以简单地调用 populate() 方法并知道对象的属性将得到更新?
    • 通过传递更多回调 - 如果您想在属性更新时呈现页面,请将其放入函数中并将其传递到链中并在 db.collection 回调中调用它。
    • @DuxPrime:嗯,你需要把你的代码流想象成你传递给findOne的回调。因此,您将在其中继续编码。此外,重要的是要记住函数(以及因此的功能) 可以像传递任何其他数据一样传递。
    猜你喜欢
    • 1970-01-01
    • 2019-04-16
    • 2016-05-23
    • 2014-07-15
    • 2012-06-16
    • 2017-03-27
    • 2023-04-03
    • 2020-04-07
    • 1970-01-01
    相关资源
    最近更新 更多