如何覆盖延迟对象传递的闭包

时间:2013-04-11 00:58:45

标签: javascript jquery closures jquery-deferred

因此,我尝试在Web应用程序上使用$ .Deferred对象,以确保在尝试执行[目标]之前已完成[先决条件]。

长话短说,我想用它的真实代码看起来像......

function QuoteReview(openQuote) {
    //constants
    this.RETIRE_AFTER=180; //...seconds. Do not use cached values older than this, instead, reload them
    this.lastLoadedQuotes=undefined;
    this.quotes=[];
    this.currentQuote={
        chosen:false, 
        id:undefined, 
        loaded:false, 
        name:undefined,
        pricebook:undefined,
        prontoId:undefined,
        state:undefined,
        reviewer:undefined,
        opportunityId:undefined,
        accountCode:undefined,
        items:[],
        lastLoad:undefined 
    };
    var _curQuote=this.currentQuote;
    this.displayQuote=function(forceLoad) {
        //if forceLoad was specified as true, the quote has never been loaded, or the last load is past it's retirement age, load it first
        if (forceLoad || typeof this.currentQuote.lastLoad == "undefined" || ((new Date() - this.currentQuote.lastLoad)/1000)>this.RETIRE_AFTER)
            this.loadQuote().then(this._displayQuote);
        else
            this.displayQuote();
    }
    this._displayQuote=function() {
        console.log(this); //Displays the deferred object
        console.log(self); //reference error (expected)
        console.log(currentQuote); //reference error
        console.log(_curQuote); //reference error
    }
    this.loadQuote=function() {
        if (this.currentQuote.chosen) {
            var self=this;
            return $.getJSON("queries/getQuote.php?id="+encodeURIComponent(this.currentQuote.id),function(data) {
                //TODO add in the error/logged out handling
                data=data.data[0];
                self.currentQuote.name=data.name;
                self.currentQuote.pricebook=data.pricebook;
                self.currentQuote.prontoId=data.prontoId;
                self.currentQuote.state=data.state;
                self.currentQuote.reviewer=data.reviewer;
                self.currentQuote.opportunityId=data.opportunityId;
                self.currentQuote.accountCode=data.accountCode;
                self.currentQuote.items=data.items;
                self.currentQuote.lastLoad=new Date();
                self.currentQuote.loaded=true;
            });
        } else {
            console.log("tried to load quote before it was chosen!");
            return $.Deferred();
        }
    }
}

但是我已经制作了一些测试代码来更轻松地显示问题:

function getSomeAjax() {
    return $.getJSON("queries/quoteSearch.php?needle=",function(data) {
        console.log("got the response");
        console.log(data);
        window.someInfo=data;
    });
}
function _useIt() {
    console.log("we would use the data here, doing some dom type stuff");
    console.log(window.someInfo);
    console.log(this);
}
function useIt() {
    getSomeAjax().then(_useIt);
}

(该查询只返回一些数据......在这种情况下它并不重要,我们也不做任何事情)

问题是,当我在this函数中记录_useIt()时,我看到了延迟对象的范围。现在这是完全有道理的,我当时正在调用 - 延迟对象的成员,但有没有人知道解决这个问题的方法?如果我使用.apply(window,[]),它似乎甚至不会调用_useIt。现在,看到我只是在页面上使用quoteReview = new QuoteReview();并且我将始终只有一个QuoteReview对象,我知道我可以quoteReview.currentQuote而不是this.currentQuote,我不会&# 39;如果推动推动,请注意这一点,但我也想知道这样做的正确方法。

回答后编辑: 在这种情况下,我最终结合使用了Travis和Esthete的答案。我发现Esthete在回调函数中为我工作,但后来我意识到我还需要创建一个_curParts_curQuotes_curPart变量;我也使用在var self=this内分配的loadQuote()来访问匿名ajax函数中的成员,但它似乎是双重工作(考虑self也具有_curQuote的所有属性})。相反,我在整个构造函数的范围内使用了var self=this,并使用它来访问匿名ajax函数中的成员以及作为回调传递给.then()的函数。

3 个答案:

答案 0 :(得分:3)

你应该能够使用闭包来解决问题,虽然我觉得这不是最好的解决方案。

var _curQuote = this.currentQuote;
this._displayQuote=function() {
  // Use _curQuote, which should just reference the member currentQuote.
}

看一下this fiddle,希望能够澄清一些混乱。

答案 1 :(得分:1)

您需要使用闭包和新的范围变量。 this取决于调用上下文,并将像您所见的那样分配给jQuery对象。

您可以尝试这样的事情(从您的代码中剪掉):

this.loadQuote=function() {
    var self = this; // This is grabbing the correct context
    return $.getJSON("local/address.php",function(data){
        //success handler. Fills in the this.currentQuote object
        // Here you'll want to use self.currentQuote
    });
}

编辑:我想提供一些示例来澄清一些评论。我个人的偏好是Underscore.js绑定方法。如果您没有使用该框架,您可以包含您自己的该方法版本,然后将其投放到函数原型上以进行更清晰的调用。

var MyClass = function() {
    this.MyVariable = "Hello World";

    this.MyCallback = function(data) {
        alert(this.MyVariable);
    };

    // Using 'self' variable to preserve invocation context
    //    The important thing is that 'self' is LOCAL ONLY
    //    and the MyCallback method can reliably rely on 'this'
    this.MakeCallSelfVariable = function() {
        var self = this;
        return $.getJSON("url", function(data) {
            self.MyCallback(data);
        }
    };

    // Using Underscore.js _.bind()
    this.MakeCallUnderscore = function() {
        return $.getJSON("url", _.bind(this.MyCallback, this));
    };

    // Using jQuery's options to set the context (obviously
    //    depends on jQuery
    this.MakeCallJQuery = function() {
        return $.ajax({
            dataType: "json",
            url: "url",
            context: this, // Important line
            success: function(data) {
                // Here, 'this' will refer to what you
                //    assigned to context
                this.MyCallback(data);
            }
        });
    };
};

答案 2 :(得分:1)

this的含义并不总是很明显,我不想猜测_useIt()中的含义。它可以是全局命名空间或其中的任何内容,具体取决于代码的组织方式。

this放在一边,首先要做的是避免通过全局成员传递data。当getSomeAjax()返回符合Promise的jqXHR时,data会自动传递给已链接的.then().done()处理程序。

例如,您的代码将简化如下:

function getSomeAjax() {
    return $.getJSON("queries/quoteSearch.php?needle=", function(data) {
        console.log("got the response");
        console.log(data);
    });
}
function _useIt(data) {
    console.log("we would use the data here, doing some dom type stuff");
    console.log(data);
}
function useIt() {
    getSomeAjax().then(_useIt);
}

...,匿名撰写的_useIt可能更容易理解:

function getSomeAjax() {
    return $.getJSON("queries/quoteSearch.php?needle=", function(data) {
        console.log("got the response");
        console.log(data);
    });
}
function useIt() {
    getSomeAjax().then(function(data) {
        console.log("we would use the data here, doing some dom type stuff");
        console.log(data);
    });
}

请注意,data显示为匿名函数的第一个参数,其方式与$.getJSON()回调的第一个参数相同。第二个和第三个参数同样可以在两个函数中使用。

此外,.then()的力量可能不是必需的。 .done()将完成这项工作,除非你需要通过返回一个新的值来过滤Promise,或者在扩展的方法链上传递一个新的Promise,例如。 getSomeAjax(...).then(...).then(...).done(...).fail(...).always(...)