Javascript setInterval и решение `this` - PullRequest
60 голосов
/ 01 мая 2010

Мне нужно получить доступ к this из моего setInterval обработчика

prefs: null,
startup : function()
    {
        // init prefs
        ...
        this.retrieve_rate();
        this.intervalID = setInterval(this.retrieve_rate, this.INTERVAL);
    },

retrieve_rate : function()
    {
        var ajax = null;
        ajax = new XMLHttpRequest();
        ajax.open('GET', 'http://xyz.com', true);
        ajax.onload = function()
        {
            // access prefs here
        }
    }

Как я могу получить доступ к this.prefs в ajax.onload?

Ответы [ 9 ]

85 голосов
/ 01 мая 2010

Строка setInterval должна выглядеть следующим образом: -

 this.intervalID = setInterval(
     (function(self) {         //Self-executing func which takes 'this' as self
         return function() {   //Return a function in the context of 'self'
             self.retrieve_rate(); //Thing you wanted to run as non-window 'this'
         }
     })(this),
     this.INTERVAL     //normal interval, 'this' scope not impacted here.
 ); 

Редактировать : Тот же принцип применяется к "onload". В этом случае обычно «внешний» код мало что делает, он просто устанавливает запрос и затем отправляет его. В этом случае дополнительные накладные расходы на дополнительную функцию, как в приведенном выше коде, не нужны. Ваш retrieve_rate должен выглядеть примерно так: -

retrieve_rate : function()
{
    var self = this;
    var ajax = new XMLHttpRequest();
    ajax.open('GET', 'http://xyz.com', true);
    ajax.onreadystatechanged= function()
    {
        if (ajax.readyState == 4 && ajax.status == 200)
        {
            // prefs available as self.prefs
        }
    }
    ajax.send(null);
}
77 голосов
/ 12 февраля 2014
this.intervalID = setInterval(this.retrieve_rate.bind(this), this.INTERVAL);
18 голосов
/ 23 октября 2011

Поведение по умолчанию setInterval - привязка к глобальному контексту. Вы можете вызвать функцию-член, сохранив копию текущего контекста. Внутри retrieve_rate переменная this будет правильно привязана к исходному контексту. Вот как будет выглядеть ваш код:

var self = this;
this.intervalID = setInterval(
    function() { self.retrieve_rate(); },
    this.INTERVAL);

Дополнительный совет: для простой ссылки на функцию (в отличие от ссылки на объект, которая имеет функцию-член) вы можете изменить контекст, используя call или apply методы JavaScript.

11 голосов
/ 16 июля 2016

С улучшенной поддержкой браузера теперь можно использовать расширение EcmaScript 6, метод стрелки => , чтобы правильно сохранить this.

startup : function()
    {
        // init prefs
        ...
        this.retrieve_rate();
        this.intervalID = setInterval( () => this.retrieve_rate(), this.INTERVAL);
    },

Использование метода => сохраняет this, когда retrieve_rate() вызывается интервалом. Нет необходимости в фанки-себе или передаче this в параметрах

7 голосов
/ 15 октября 2016

window.setInterval(function(){console.log(this)}.bind(this), 100)

это допустимо в javascript и сохраняет много кода:)

2 голосов
/ 05 ноября 2014

Это было бы самое чистое решение, так как большую часть времени вы фактически хотите переключить контекст this для своих последовательных вызовов метода:

Также легче понять концепцию.

    // store scope reference for our delegating method
    var that = this;
    setInterval(function() {
        // this would be changed here because of method scope, 
        // but we still have a reference to that
        OURMETHODNAME.call(that);
    }, 200);
0 голосов
/ 18 сентября 2018

В современных браузерах метод setInterval допускает дополнительные параметры, которые передаются в функцию, указанную func, после истечения таймера.

var intervalID = scope.setInterval (func, delay [, param1, param2, ...]);

Следовательно, возможное решение может быть:

this.intervalID = setInterval(function (self) {
        self.retrieve_rate();
    }, this.INTERVAL, this);

Демо:

var timerId;
document.querySelector('#clickMe').addEventListener('click', function(e) {
    timerId = setInterval(function (self) {
        self.textContent = self.textContent.slice(0, -1);
        if (self.textContent.length == 0) {
            clearInterval(timerId);
            self.textContent = 'end..';
        }
    }, 250, this);
})
<button id="clickMe">ClickMe</button>
0 голосов
/ 01 мая 2010
prefs: null,
startup : function()
    {
        // init prefs
        ...
        this.retrieve_rate();
        var context = this;
        this.intervalID = setInterval(function()
                                      {
                                          context.retrieve_rate();
                                      }, this.INTERVAL);
    },

retrieve_rate : function()
    {
        var ajax = null;
        ajax = new XMLHttpRequest();
        ajax.open('GET', 'http://xyz.com', true);
        var context = this;
        ajax.onload = function()
        {
            // access prefs using context.
            // e.g. context.prefs
        }
    }
0 голосов
/ 01 мая 2010

Это не красивое решение, но оно обычно используется:

var self = this;
var ajax = null;
//...
ajax.onload = function() {
    self.prefs....;
}
...