【问题标题】:Javascript multiple prototype functions - how to call one from anotherJavascript多个原型函数 - 如何从另一个调用一个
【发布时间】:2014-04-04 08:00:30
【问题描述】:

我最近一直致力于编写更简洁的 Javascript 代码,以及使用带有原型的对象等。但我在某些方面感到困惑......

function TimeCard(){
    this.date               = new Date();
    this.pay_period_begin   = null;
    this.pay_period_end     = null; 
}

这是我的时间卡对象,其中包含一些初始值。我已经编写了一堆函数,还有更多函数是该时间卡的一部分,如果我理解正确,它们将是原型函数。以下是我目前掌握的一些内容:

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()的函数吗?
  • 是的,它已经在selectAll函数下显示的代码中了。

标签: javascript oop prototype


【解决方案1】:

我的问题是,当我尝试调用 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
        });
    }
    // ...
};

如果我在TimeCard 对象上调用this.doSomething();,则在调用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 shim”(因此我在上面提到了$.proxy;您知道您有):

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

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

在上述所有情况下,您将失去 this 引用 DOM 元素的好处。在这种特殊情况下,您可能并不关心,但如果您这样做了,您可以从事件对象的 currentTarget 属性中获取它。例如,这会调用 tc.selectAllthis 引用 tc 并传入 would 已经是 this(您挂钩处理程序的 DOM 元素)作为第一个参数:

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

另一个不太可能的可能性与您更新TimeCard.prototype 的方式有关。按照您的操作方式,可以通过 new TimeCard() 创建对象 before 替换 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;
})();

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

【讨论】:

  • @thefourtheye:谢谢。确实如此。事实上,人们确实如此。 :-) 右上角有一个 RSS 提要链接,但任何体面的阅读器都会自动发现,如果你只是张贴地址。给我留言告诉我你遇到的问题? tj.crowder@farsightsoftware.com
  • 像往常一样史诗般的回答
  • 谢谢,我开始明白了。尽管我认为这些都不是我的问题——我所指的实际调用只是在我的原型中的 selectAll 函数中。所以这些原型由于某种原因看不到对方。但我确实喜欢你给出的最后一个例子,我必须尝试一下。
  • @Tanner:这可能将问题推到了一个层次:selectAll 是如何被调用的?因为如果selectAll 被用作回调(事件处理程序、ajax 处理程序、setTimeout 回调等),那么在调用selectAll 期间,this 将不再指向该对象。
  • @Tanner:十分之九,你已经有了一个可以关闭的有用上下文,所以我倾向于这样做。但是如果你不这样做,我可能会倾向于使用 shim 并使用 Function#bind。但在那时,它基本上是一种风格。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
相关资源
最近更新 更多