Закрытие Google: передача "this" в window.setInterval

У меня есть файл модели JS, который выглядит как

goog.provide('model.ErrorLogger');
/**
 * @constructor
 */
model.ErrorLogger = function() {
    window.onerror = goog.bind(this.errorHandler, this);
    this.doInitialSend();

};
goog.addSingletonGetter(model.ErrorLogger);

model.ErrorLogger.prototype.ws_ErrLgr_config = true;

model.ErrorLogger.prototype.doInitialSend = function(){
    if (this.ws_ErrLgr_config){
        window.setInterval(this.sendReport, this.ws_ErrLgr_config);
    }   

};

model.ErrorLogger.prototype.sendReport = function(){
    // the value of 'this' needs to be of the ErrorLogger model and not windows
    if (!this.ws_ErrLgr_config || this.stopped) {
          //some more code here
    }
}

В конструкторе я вызываю функцию doInitialSend, которая устанавливает window.setInterval. Теперь в функции sendReport значение this не является правильным. Как правильно передать "this", чтобы получить правильное значение вместо окна "this". Я попытался сохранить значение этого в ссылке, но это тоже не сработало. Например

var that = this;
window.setInterval(that.sendReport, that.ws_ErrLgr_config);

3 ответа

Решение

Идиоматический способ сделать это в Google Closure использует goog.bindс тем преимуществом, что гарантировано, что он всегда будет работать. Плюс это будет использовать Function.prototype.bind() под капотом, когда это доступно.

В этом случае решение будет:

myIntervalInMilliseconds = 1000; // One second.
window.setInterval(goog.bind(this.sendReport, this), myIntervalInMilliseconds);

С помощью that = this работает, но требует, чтобы вы явно обернули свою функцию в другую, чтобы захватить that как this в желаемой функции.

Это намного лучше, используя Function.prototype.bind() для этого, как указано в других ответах. Однако это не сработает, если вы хотите поддерживать старые браузеры (IE <9).


PS: Еще одна проблема в вашем коде в том, что он использует this.ws_ErrLgr_config в качестве интервала, который установлен в true в прототипе. Это неверно, вы должны выбрать число для представления вашего интервала.

Или это:

window.setInterval((function() {
    this.sendReport();
}).bind(this), this.ws_ErrLgr_config.ReportInterval);

Вы можете сделать это:

var that = this;
window.setInterval(function() {
    that.sendReport()
}, this.ws_ErrLgr_config.ReportInterval);

Таким образом, вы можете вызвать sendReport в правильном контексте, также это работает:

 window.setInterval(this.sendReport.bind(this), this.ws_ErrLgr_config.ReportInterval);

Причина по которой window.setInterval(that.sendReport, this.ws_ErrLgr_config.ReportInterval) не работает, потому что Javascript передается по значению. Вышеупомянутое утверждение эквивалентно:

window.setInterval(function(){
    // the value of 'this' needs to be of the ErrorLogger model and not windows
    if (!this.ws_ErrLgr_config || this.stopped) {
          //some more code here
    }
}, this.ws_ErrLgr_config.ReportInterval);

Используя ключевое слово.bind() или поместив его в другую функцию, а затем ссылаясь на thatиз внешнего прицела вы можете вызвать функцию в нужной вам области.