【问题标题】:Javascript setInterval and `this` solutionJavascript setInterval 和 `this` 解决方案
【发布时间】:2011-02-14 12:12:32
【问题描述】:

我需要从我的setInterval 处理程序访问this

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

如何访问ajax.onload 中的this.prefs?

【问题讨论】:

标签: javascript this setinterval


【解决方案1】:
this.intervalID = setInterval(this.retrieve_rate.bind(this), this.INTERVAL);

【讨论】:

  • 这是正确的解决方案。公认的解决方案似乎需要更多不必要的代码。
  • 但是这种方法有一个缺点。很可能,它不适用于旧版本的 IE
  • @Nechehin 值得注意。但它仍然是一个更清洁的解决方案。
  • 从 IE9 开始支持它,所以对我来说是一个干净的解决方案。
  • 如果您需要对 IE8 的支持并且您正在使用Underscore.js,您可以使用_.bindthis.intervalID = setInterval(_.bind(this.retrieve_rate, this), this.INTERVAL);
【解决方案2】:

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:它对这种场景和循环一样有用。
  • @Anthony:所以self 的技巧是这里唯一的选择吗?你能确认 Matthew 的解决方案行不通吗?
  • @Michael:首先,它不是“技巧”,它只是 Javascript 中的工作方式。在撰写此评论时,马修目前的回答无效。它的早期版本可能有效,但它涉及将this 作为参数传递,这是不必要的和尴尬的(retrieve_rate 的任何调用者都会知道这个不必要的特殊要求)。
  • 在 setInterval 中将 this 作为参数传递给 (function(self){...})(this) 对我不起作用,因为该函数会立即执行而不是延迟。 @Joel Fillmore 的解决方案对我有用
【解决方案3】:

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

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

额外提示:对于普通函数引用(相对于具有成员函数的对象引用),您可以使用 JavaScript 的 callapply 方法更改上下文。

【讨论】:

  • 这对我有用,但似乎不需要调用“调用”。 retrieve_rate 的上下文默认设置为 self,因为它是作为成员函数调用的。
  • @Dreendle - 你是对的,我记得在需要的地方为回调函数引用解决了这个问题。我已经确定了答案,谢谢!
【解决方案4】:

随着浏览器支持的改进,现在可以使用EcmaScript 6 enhancement, the arrow => method,正确保存this

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

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

【讨论】:

    【解决方案5】:

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

    这在 javascript 中是合法的,并且可以节省大量代码 :)

    【讨论】:

      【解决方案6】:

      这将是最干净的解决方案,因为大多数时候您实际上希望为连续的方法调用切换 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);
      

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:
          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 吗?
          • 这已经朝着正确的方向发展,尽管不需要将上下文作为参数传递。
          • @Matthew, Anthony 那么如何从 onload 访问它?试过this.prefs,但没有用...
          • 不客气,马修,顺便说一句,你不需要使用callcontext.retrieve_rate() 就足够了,因为你有一个 基础对象 (@ 987654328@)
          • 再次感谢@Anthony 和@CMS。 :) 当闭包在起作用时,很容易变得不必要的“聪明”。
          【解决方案9】:

          这不是美容解决方案,但很常用:

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

          【讨论】:

          • 问题是setInterval如何调用retrieve_rate函数,方法里面的this值是指全局对象...
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-11-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多