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

时间:2014-03-03 10:15:18

标签: javascript oop prototype

我最近一直在努力制作更干净的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();。我确信这不是解决这个问题的正确方法,但究竟是什么?

1 个答案:

答案 0 :(得分:8)

  

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

听起来this不再是指您的实例了。您必须向我们展示我们确实要求的实际调用,但在JavaScript中,this主要由函数调用的方式设置,而不是在其定义的位置,因此它是this最终变得与众不同。

这是一个假设的例子:

TimeCard.prototype = {
    // ...
    doSomething: function() {
        // here, `this` probably refers to the timecard
        someArray.forEach(function() {
            this.getTimeCardData(); // <=== Problem, `this` has changed
        });
    }
    // ...
};

如果我在this.doSomething();对象上拨打TimeCard,则在通话this内会引用时间卡。但在forEach回调中,this将不再引用时间卡。所有类型的回调都会发生同样的变化; ajax等。

要解决此问题,您可以记住变量this

TimeCard.prototype = {
    // ...
    doSomething: function() {
        var thisCard = this;
        someArray.forEach(function() {
            thisCard.getTimeCardData(); // <=== Problem
        });
    }
    // ...
};

根据您的具体情况,还有其他各种方法可以解决这个问题。例如,您有selectAll来电getTimeCardData。但是假设使用错误的selectAll值调用this?在你的评论中,你说你这样做:

$('#container').on('click', '#selectAll', tc.selectAll);

这意味着当调用selectAll时,this将引用DOM元素,而不是您的对象。

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

  1. 由于你正在使用jQuery,你可以使用$.proxy,它接受​​一个函数和一个值作为this,并返回一个新的函数,当被调用时,将调用将this设置为所需值的原始文件:

    $('#container').on('click', '#selectAll', $.proxy(tc.selectAll, tc));
    
  2. 使用ES5的Function#bind,它做同样的事情。请注意IE8及更早版本没有它,除非你包含一个“ES5垫片”(因此我注意到$.proxy以上;你知道你有那个):

    $('#container').on('click', '#selectAll', tc.selectAll.bind(tc));
    
  3. 使用闭包(不要让名字打扰你,闭包不复杂): 更多(在我的博客上)

    $('#container').on('click', '#selectAll', function() {
        tc.selectAll();
    });
    
  4. 在上述所有情况中,您将失去this引用DOM元素的好处。在这种特殊情况下,您可能并不关心,但如果您这样做,则可以从事件对象的currentTarget属性中获取它。例如,这会调用tc.selectAll this引用tc并传入已经this(您挂钩处理程序的DOM元素) on)作为第一个论点:

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

    另一种不太可能的可能性与您如何更新TimeCard.prototype有关。你正在这样做,可以通过new TimeCard() 创建对象,然后替换TimeCard.prototype对象运行的代码,这意味着他们将拥有旧的原型。

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

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

    ...主要是因为它可以防止时间问题。