Javascript多个原型函数-如何从一个调用另一个

Javascript多个原型函数-如何从一个调用另一个,javascript,oop,prototype,Javascript,Oop,Prototype,最近,我一直在努力制作更干净的Javascript代码,并将对象与原型一起使用等等。但我对一些问题感到困惑 function TimeCard(){ this.date = new Date(); this.pay_period_begin = null; this.pay_period_end = null; } 这是我的timecard对象,带有一些初始值。我有一大堆我已经写过的函数,还有更多的函数,它们是时间卡的一部分,如

最近,我一直在努力制作更干净的Javascript代码,并将对象与原型一起使用等等。但我对一些问题感到困惑

function TimeCard(){
    this.date               = new Date();
    this.pay_period_begin   = null;
    this.pay_period_end     = null; 
}
这是我的timecard对象,带有一些初始值。我有一大堆我已经写过的函数,还有更多的函数,它们是时间卡的一部分,如果我理解正确的话,它们将是原型函数。以下是我目前掌握的一些信息:

TimeCard.prototype = {         
    init : function(){
        this.pay_period_begin   = $("#pay_period_begin");
        this.pay_period_end     = $("#pay_period_end");
    },
    getTimeCardData : function(){
          //ajax request
    },
    selectAll : function(){
        this.getTimeCardData();
    }
    ...
};
我的问题是,当我尝试调用
this.getTimeCardData()
时,它说我的对象没有这样的方法。很明显,我可以访问其他变量,因为它们是在我的构造函数中声明的,但我不理解原型的作用域是如何定义的。到目前为止,我已经通过使用
tc.getTimeCardData()
而不是
this.getTimeCardData()
解决了这个问题,其中
tc
是我在外部声明的对象的实例-
var tc=new TimeCard()。我确信这不是正确的方法,但什么是正确的

我的问题是,当我尝试调用
this.getTimeCardData()
时,它说我的对象没有这样的方法

听起来好像
这个
不再引用您的实例。当然,您必须向我们展示实际的调用,但是在JavaScript中,
这个
主要是通过调用函数的方式来设置的,而不是通过定义函数的位置来设置的,因此,
这个
很容易变成不同的东西

下面是一个假设的例子:

TimeCard.prototype = {
    // ...
    doSomething: function() {
        // here, `this` probably refers to the timecard
        someArray.forEach(function() {
            this.getTimeCardData(); // <=== Problem, `this` has changed
        });
    }
    // ...
};
根据您的具体情况,还有其他各种解决方法。例如,您有
selectAll
调用
getTimeCardData
。但是,假设调用
selectAll
时使用了错误的
值?在你的评论中,你说你是这样做的:

$('#container').on('click', '#selectAll', tc.selectAll);
function TimeCard(){
    this.date               = new Date();
    this.pay_period_begin   = null;
    this.pay_period_end     = null; 
}
TimeCard.prototype.getTimeCardData = function(){
      //ajax request
};
// ...
这意味着当调用
selectAll
时,
this
将引用DOM元素,而不是对象

在这种特定情况下,您有三种选择:

  • 由于您使用的是jQuery,因此可以使用
    $.proxy
    ,它接受一个函数和一个值作为
    this
    ,并返回一个新函数,调用该函数时,将调用原始函数,并将
    this
    设置为所需值:

    $('#container').on('click', '#selectAll', $.proxy(tc.selectAll, tc));
    
  • 使用ES5的
    函数#bind
    ,它做同样的事情。请注意,IE8和更早版本没有它,除非您包括一个“ES5垫片”(因此我在上面提到了
    $.proxy
    ,您知道您有):

  • 使用闭包(不要让名称困扰您,闭包并不复杂): 更多(在我的博客上):

  • 在上述所有情况下,您将失去引用DOM元素的
    这个
    的好处。在这种特殊情况下,您可能不在乎,但如果在乎,您可以从事件对象的
    currentTarget
    属性中获得它。例如,这将调用
    tc。使用
    this
    引用
    tc
    并将本应是
    this
    的内容(钩住处理程序的DOM元素)作为第一个参数选择All

    $('#container').on('click', '#selectAll', function(e) {
        tc.selectAll(e.currentTarget);
    });
    

    另一个不太可能的可能性与您如何更新
    TimeCard.prototype
    有关。按照您的方式,可以在替换
    TimeCard.prototype的代码运行之前,通过
    new TimeCard()
    创建对象,这意味着它们将拥有旧的原型

    通常,我强烈建议不要替换为构造函数的
    prototype
    属性自动创建的对象。相反,只需添加到已经存在的对象,如下所示:

    $('#container').on('click', '#selectAll', tc.selectAll);
    
    function TimeCard(){
        this.date               = new Date();
        this.pay_period_begin   = null;
        this.pay_period_end     = null; 
    }
    TimeCard.prototype.getTimeCardData = function(){
          //ajax request
    };
    // ...
    
    原因如下:时间。如果在
    prototype
    属性上替换对象,则在替换之前通过
    new TimeCard()
    创建的任何对象都将使用旧原型,而不是新原型

    我还建议始终在作用域函数中创建这些,以便您知道声明和原型添加同时发生:

    var TimeCard = (function() {
        function TimeCard(){
            this.date               = new Date();
            this.pay_period_begin   = null;
            this.pay_period_end     = null; 
        }
        TimeCard.prototype.getTimeCardData = function(){
              //ajax request
        };
        // ...
    
        return TimeCard;
    })();
    

    …主要是因为它可以防止计时问题。

    您能告诉我们调用this.getTimeCardData()的函数吗?是的,它已经在
    selectAll
    函数下显示的代码中了。@theourtheye:谢谢。是的。事实上,人们确实如此。:-)右上角有一个RSS提要链接,但是如果你只是把地址发进去,任何体面的读者都会自动发现。给我写张便条,告诉我你有什么问题?tj。crowder@farsightsoftware.comepic答案和往常一样,汉克斯,我开始明白了。虽然这两个都不是我的问题,但我认为我所指的实际调用只是在原型中的
    selectAll
    函数中。因此,由于某种原因,这些原型无法看到对方。但我很喜欢你最后给出的例子,我必须试试。@Tanner:这可能把问题推到了一个层次:调用
    selectAll
    的方式如何?因为如果
    selectAll
    被用作回调(事件处理程序、ajax处理程序、
    setTimeout
    回调等),那么在调用
    selectAll
    的过程中,
    this
    将不再指向对象。@Tanner:十有八九,您已经有了一个有用的上下文可以关闭,所以我倾向于这样。但如果你没有,我可能倾向于使用垫片和
    函数#bind
    。但在这一点上,它基本上是一种风格。
    var TimeCard = (function() {
        function TimeCard(){
            this.date               = new Date();
            this.pay_period_begin   = null;
            this.pay_period_end     = null; 
        }
        TimeCard.prototype.getTimeCardData = function(){
              //ajax request
        };
        // ...
    
        return TimeCard;
    })();