JavaScript - setInterval用传递的函数不能识别全局变量

3

我遇到了一个奇怪的问题,但无法理解Angular为什么会这样表现,我在谷歌上搜索问题时并没有太多的好运,所以我来到这里。

我想测试setInterval()函数和一些变量的计数,不是太难的事情,但我卡住了,找不到解决方案或者解释。

这是我正在使用的代码,它可以正常工作:

public counter: number = 1;

  myFunction() {
    setInterval(() => {
      console.log(this.counter);
      this.counter++;
    }, 1000);
  }

Output: 1, 2, 3, 4, 5...

这段代码本来可以正常运行,但是当我把函数改成这样时,输出变成了未定义,然后是 Nan, Nan, Nan

public counter: number = 1;

  foo() {
    console.log(this.counter);
    this.counter++;
  }

  myFunction() {
    setInterval(this.foo, 1000);
  }

* myFunction is the starting function *

Output: undefined, Nan, Nan, Nan...

我猜测变量访问存在问题,导致foo()无法访问全局变量,但这是如何发生的?我该怎么解决这个问题呢?


1
setInterval(() => this.foo(), ...)setInterval(this.foo.bind(this), ...) 或者... 这与 Angular/TS/ES6 无关,可以参考任何 JS 中的 this 作用域问题。 - jonrsharpe
2个回答

6
这里有一段代码,可以实现你描述的功能。
 class A{
 public counter: number = 1;

   foo() {
     alert(this.counter);
     this.counter++;
   }

   myFunction() {
     setInterval(this.foo, 1000);
   }
 }

 const a = new A();
 a.myFunction(); // output andefined, Nan, Nan ...

现在的代码使用了bind函数:JSFiddle

 class A{
 public counter: number = 1;

   foo() {
     alert(this.counter);
     this.counter++;
   }

   myFunction() {
     setInterval(this.foo.bind(this), 1000);
   }
 }

 const a = new A();
 a.myFunction(); // output 1, 2, 3, 4 ...

这是一个非常棘手的问题,所以

首先请查看这个问题:如何在回调函数中访问正确的“this”?

还有关于这个主题的几个非常好的答案:这里这里


现在是bind方法 - Function.prototype.bind()

(来自文档)

bind()方法创建一个新函数,当调用该函数时,其this关键字设置为提供的值,并在调用新函数时提供任何提供的一系列参数之前。

并且喜欢这个解释(从文档的示例部分中提取):

bind()最简单的用法是创建一个函数,无论如何调用它,都会使用特定的this值进行调用。

新 JavaScript 程序员经常犯的一个错误是从对象中提取方法,然后稍后调用该函数,并期望它使用原始对象作为其this(例如,在基于回调的代码中使用该方法)。 然而,如果没有特殊的注意,原始对象通常会丢失。

使用原始对象从函数创建绑定函数可以很好地解决这个问题。


2
在第一个例子中,您传递了一个使用es6简写声明的函数(链接在这里),因此"this"将绑定到当前作用域。
在第二个例子中,您传递了一个对函数的引用,因为setTimeout使用指向全局对象的this执行函数,所以"this"等于window对象,因此属性未定义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接