Javascript setInterval和`this`解决方案


70

我需要thissetInterval处理程序访问

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

Answers:


93

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

我本来打算这样做,但是后来我想起了这种模式对于循环确实非常有用。
马修·弗拉申

@Matthew Flaschen:在这种情况下,它和循环一样有用。
Andy

@Anthony:所以这self是唯一的选择吗?您可以确认Matthew的解决方案行不通吗?
巴勃罗(Pablo)2010年

@Michael:首先,它不是“技巧”,而是它在Javascript中的工作方式。在撰写此评论时,Matthew的当前回答无效。它有一个较早的版本可能会起作用,但它涉及this作为不必要和麻烦的参数传递(任何调用者都retrieve_rate将知道此不必要的特殊要求)。
AnthonyWJones

1
在setInterval中this作为参数传递(function(self){...})(this)对我不起作用,因为该函数立即执行而不是被延迟。@Joel Fillmore的解决方案对我有用
Homan

92
this.intervalID = setInterval(this.retrieve_rate.bind(this), this.INTERVAL);

13
这是正确的解决方案。公认的解决方案似乎需要不必要的代码。
尼克

5
但是这种方法有一个缺点。最有可能的是,它不适用于旧版本的IE
Nechehin 2014年

@Nechehin值得一提。但是它仍然是一个更清洁的解决方案。
connorbode 2015年

自IE9以来一直受支持,因此对我来说是一个干净的解决方案。
barbara.post 2015年

2
如果您需要IE8支持你使用Underscore.js,你可以使用_.bindthis.intervalID = setInterval(_.bind(this.retrieve_rate, this), this.INTERVAL);
gfullam

18

的默认行为setInterval是绑定到全局上下文。您可以通过保存当前上下文的副本来调用成员函数。在retrieve_rate内部,该this变量将正确绑定到原始上下文。您的代码如下所示:

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

温馨提示:对于纯函数引用(与具有成员函数的对象引用相反),您可以使用JavaScriptcallapply方法来更改上下文。


1
这对我有用,但是似乎不需要调用“ call”。默认情况下,retrieve_rate的上下文应设置为self,因为它被称为成员函数。
Dreendle

@Dreendle-是的,我记得在需要回调函数引用的地方解决了此问题。我已经确定了答案,谢谢!
乔尔·菲尔莫尔

13

通过改善浏览器支持,现在可以使用EcmaScript 6增强功能arrow=>方法this正确保存了。

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

使用=>方法保留间隔调用的thiswhen retrieve_rate()。无需时髦的自我或传递this参数



2

这将是最干净的解决方案,因为大多数时候您实际上想为连续的方法调用切换此上下文:

同样,更容易掌握概念。

    // 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);

1

在现代浏览器中,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
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
        }
    }

this在传递给的函数内部setInterval,将引用全局对象。您是context.retrieve_rate不是要说this.retrieve_rate
Christian C.Salvadó10年

这已经朝着正确的方向发展了,尽管不需要将上下文作为参数传递。
AnthonyWJones

@Matthew,Anthony那么如何从onload访问它?尝试过this.prefs,但是没有用...
Pablo

不客气马修,顺便说一句,你不需要使用callcontext.retrieve_rate()就足够了,因为你有一个基本对象context.
基督教C. SALVADO

再次感谢@Anthony和@CMS。:)在进行闭包操作时,很容易不必要地变得“聪明”。
马修·弗拉申

-1

那不是一个美丽的解决方案,但它是常用的:

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

2
问题是如何setInterval调用retrieve_rate函数,this方法中的值引用全局对象...
Christian C.Salvadó2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.