javascript中具有作用域的SetInterval

javascript中具有作用域的SetInterval,javascript,Javascript,您需要使用或重新分配上下文,因为以这种方式调用的函数会将其上下文丢失到全局范围。这在下面的示例中最容易观察到 var i=0; var t={ a:function(){ var that=this; this.timer=setInterval(function(){ that.b(); },30); }, b:function(){ if(i++<1){ console.log(this); } } };

您需要使用或重新分配上下文,因为以这种方式调用的函数会将其上下文丢失到全局范围。这在下面的示例中最容易观察到

var i=0;
var t={
  a:function(){
    var that=this;
    this.timer=setInterval(function(){
      that.b();
    },30);
  },
  b:function(){
    if(i++<1){
      console.log(this);
    }
  }
};
t.a();
以下是您案例中的一些解决方案示例

o = { // an object
    foo: 1,
    bar: function () { // function assuming `this` is `o`
        return this.foo;
    }
};
f = o.bar; // function `f` loses scope of `o`
o.bar();   // 1,         o.foo is defined
f();       // undefined, window.foo is undefined
在这些示例中,您将
作为第三个参数传递,即

// bind
function scopedInterval(func, delay, context) {
    return window.setInterval(func.bind(context), delay);
}
// or call
function scopedInterval(func, delay, context) {
    return window.setInterval(function () {func.call(context);}, delay);
}


如果不这样做,
setInterval
的上下文是
window
(我总是将其作为
window.setInterval
调用,所以我不会忘记这一点)

正如Crockford在他的新书《Javascripts:好的部分》中所说:

当函数不是对象的属性时,它将作为函数调用[这是setInterval的情况]。使用此模式调用函数时,“this”绑定到全局对象。这是语言设计中的一个错误。如果语言设计正确,当调用内部函数时,“this”仍将绑定到外部函数的“this”变量。此错误的结果是,方法无法使用内部函数来帮助其完成工作,因为内部函数不共享方法对对象的访问权限,因为其“this”绑定到错误的值。[然后他谈到与你发现的相同的解决方法]


你想知道为什么这不起作用是对的,对我来说,所有其他答案都不能正确地解决这个问题。事实上,这是毫无意义的,正如Crockford所说,这是语言设计中的一个错误,可能会让人感到困惑

所以如果你知道如何解决这个问题。。。你为什么要问?@migg我想知道为什么:)请不要把上下文(thisArg的
thisArg
)与[variable]作用域混淆。这并不能解释为什么。这是因为它是language@Geeo我不能+1你回答的唯一原因是(正如你在评论中重复的)你说“这是语言的一个缺陷”。这是一种观点,而我同意JavaScript有一个愚蠢的定义,即<代码> < < /> >在这些情况下应该如何工作,它可能会“打扰人”,我不认为它是“bug”。顺便说一下,它比“代码> >调用<代码>在本例中要简单得多:只是代码>上下文。函数()(代码)> @ PaulS。是的,这不是一个真正的错误,仍然是一个糟糕的设计。谢谢,我还没有读那本书,我会完成阅读惊人的书的时候!Crockford正确地指出,
不应引用全局对象,但是我认为如果
这个
引用函数实例化时在词汇封闭范围内的任何内容,会引起各种各样的奇怪。@Pointy你能详细解释一下你为什么这么认为吗?@Geeo我得花点时间考虑一下,但它在我的脑海中敲响了警钟:-)我知道,在未来的语言版本中,有些情况下会出现这种情况(我认为是“和谐”)。
// bind
function scopedInterval(func, delay, context) {
    return window.setInterval(func.bind(context), delay);
}
// or call
function scopedInterval(func, delay, context) {
    return window.setInterval(function () {func.call(context);}, delay);
}
this.timer = scopedInterval(this.b, 30, this);