【问题标题】:Javascript: access object scope members within higher order functionJavascript:在高阶函数中访问对象范围成员
【发布时间】:2012-10-20 10:45:29
【问题描述】:

领域模型

var Question = function(text, answerType){
    this.id = 0
    this.text = text;
}

var Form = function(){
    this.questions = [] 
    this.addQuestion = function(question){
        question.id = this.questions.length + 1
        this.questions.push(question)
    }
}

呈现表单

var FormRenderer = function(){
    this.wrapperSelector = "#wrapper"
    this.render = function(form){
       // how to access renderQuestion member of FormRenderer within the higher-order function?
       $(form.questions).each( function(){ **this.renderQuestion(form, this) }**) 
    }

    this.renderQuestion = function(form, question){
        var questionDomId = "question" + question.id

        var questionText = '<input type="text" size="75" name="questionText" value="'+ question.text +'" /><br/><br/><br/>'
        var questionWrapper  = "<div id='" + questionDomId + "'>" + questionText + "</div>"

        // how do i access wrapperSelector member of FormRender when renderQuestion is called as higher-order function?
        **$(this.wrapperSelector).append(questionWrapper)** 
    }
}

客户端代码

var q1= new Question("question1", "Text Box")
var form = new Form()
form.addQuestion(q1)

var formRenderer = new FormRenderer()
formRenderer.render(form)

问题与标题相同。我已经为上面使用 Javascript cmets 的特定示例寻求帮助。任何帮助表示赞赏。

【问题讨论】:

  • 请注意each 是高阶函数,而不是renderQuestion。顺便说一句,这在我看来像是对 each 的不当使用 - 只需使用 for 循环,您就不会遇到这些问题。

标签: javascript oop scope higher-order-functions


【解决方案1】:

您需要像这样存储对对象的引用:

var FormRenderer = function(){
    this.wrapperSelector = "#wrapper"
    this.render = function(form){
       $(form.questions).each( this.renderQuestion) ;
    }

   var self = this; // <--------------- 

   this.renderQuestion = function(form, question){
        var questionDomId = "question" + question.id

        var questionText = '<input type="text" size="75" name="questionText" value="'+ question.text +'" /><br/><br/><br/>'
        var questionWrapper  = "<div id='" + questionDomId + "'>" + questionText + "</div>"

        // now use self instead of this
        $(self.wrapperSelector).append(questionWrapper);
    }
}

【讨论】:

  • 在您的情况下,form 参数是数组索引。
【解决方案2】:

您可以在成员函数内部的this 中获取父对象实例,方法如下所示。

如果object 是您的实例,而func 是一个成员函数,您希望this 成为您的实例本身,那么您可以将成员函数调用为:

obj.func.call(obj, <params>);

而不是

object.func(<params>);

在你的情况下,这将是:

var fR = new FormRenderer(<params>);
...
fR.renderQuestion.call(fR, <params>);

您的renderQuestion 可以像在外面一样使用this。即

var FormRenderer = function(){
    this.wrapperSelector = "#wrapper"
    ...
    this.renderQuestion = function(form, question){

        // this refers to the object itself, so you can call        
        $(this.wrapperSelector).append(...);
    }
}

【讨论】:

  • fR.renderQuestion.call(fR, &lt;params&gt;); 将绝对等于 fR.renderQuestion(&lt;params&gt;);。但是,这不是问题 - OP 的问题是 this 在方法被输入 jQuery.each 时没有指向实例
【解决方案3】:

必须根据来自 Pumbaa80 和 Bergi 的意见进行一些重组以解决问题。感谢您的回答。

var FormRenderer = function(){
    this.render = function(form){
        var formEventHandler = new FormEventHandler(form)
        $(form.questions).each(new RenderQuestion(formEventHandler).render)
    }

    RenderQuestion = function(formEventHandler){
        this.formEventHandler = formEventHandler
        this.wrapperSelector = "#wrapper"
        var self = this
        this.render = function(index, question){
            var questionDomId = "question" + question.id

            // generate elements
            var questionText = '<input type="text" size="75" name="questionText" value="'+ question.text +'" /><br/>'
            var questionWrapper  = "<div id='" + questionDomId + "'>" + questionText + "</div>"

            // append to dom
            $(self.wrapperSelector).append(questionWrapper)
        }
    }
}

【讨论】:

  • 每次调用render 时实例化本地RenderQuestion 的新实例似乎没用。你为什么不直接传递一个做这些事情的函数呢?
【解决方案4】:

不要使用jQuery.each - 它会将this 引用更改为不寻常的内容。你可以像这样使用ES 5.1 bind 和普通的forEach

FormRenderer.prototype.render = function(form) {
    form.questions.forEach(this.renderQuestion.bind(this, form));
};

我建议使用经典的 for 循环,它比任何高阶事物都更容易阅读和更快:

FormRenderer.prototype.render = function(form) {
    for (var i=0; i<form.questions.length; i++)
        this.renderQuestion(form, form.questions[i]);
};

如果您确实想要/需要使用jQuery.each,则需要在变量中保留对当前FormRenderer 实例(this) 的引用:

FormRenderer.prototype.render = function(form) {
    var renderer = this;
    $.each(form.questions, function(i) {
        renderer.renderQuestion(form, this);
    });
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    • 1970-01-01
    • 2016-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多