[英]Keep babel class as `this` when using a member function in `setTimeout`
我有一个 ES2015 类,称为Foo
,它至少有两个成员函数bar
和baz
。 在bar
有一个对setTimeout
的调用,它的第一个参数是this.baz
。 在这里工作正常,我在调试器中检查了它, this
确实引用了我的类的实例。 (实际上,由于我使用的是 babel,我最终得到了一个_this = this
替换,但无论如何正确的东西被传递到setTimeout
,已确认。)
问题是,当setTimeout
回调火灾,它调用正确的函数baz
,但是this
里面baz
的价值this
是指Window
代替。 Babel 尝试在baz
开始时做一个_this2 = this
,但似乎已经太晚了。
所以我的问题出现了,在传递的函数baz
和调用它的时间之间的某个地方,它失去了this
范围。 我的问题是,我在这里用 ES2015 或 babel 做错了吗? 我觉得这是一个足够常见的用例,它不应该需要太多的压力。 就我个人而言,我想用Promise
来做这一切,但由于业务需求,我不能一次添加太多新的 JS 东西。
或者,是否有标准的习惯用法可以根据我的需要传递this
的范围? 必须将成员函数的调用对象作为其参数之一传入,这似乎非常混乱和违反直觉。
这是一个最小的工作示例供参考:
class Foo{
bar(){
setTimeout(this.baz, 1000);
}
baz(){
console.log("this message should repeat roughly once per second");
this.bar();
}
}
这是我在一个非常简单的页面上使用它的屏幕截图,以及错误消息:
编辑:我必须反对我的问题被标记为重复。 当然,在问这个问题之前,我已经搜索过setTimeout
问题。 然而,我的问题的 ES2015 和基于class
的方面是相关且重要的,因为 babel 中类的 ES2015 语法转换改变了this
的明显行为。 我的问题是关于是否有另一个 ES2015 设计模式来处理这个问题,以及为什么通过将成员函数作为要在外部调用的第一类值来破坏直观的class
抽象/封装。 我获得的最重要的见解来自@FelixKing 下面的评论,我将在这里重复一遍(以防其他人想知道):
是否讨论了自动绑定类方法(如在 Python 中)但最终决定反对,可能与语言的其余部分保持一致。 还有一个问题是是否可以在不影响内存/性能的情况下自动绑定方法。
我的问题是,我在这里用 ES2015 或 babel 做错了吗?
实际上,这是预期的 JavaScript 行为,并且与语言中的this
分配方式有关。
考虑下面的代码(没有 ES6,没有 babel ......):
var obj = {
key1: 'value1',
key2: function() {
console.log(this);
}
}
obj.key2(); //will print obj
var callback = obj.key2; //assigned the function reference to some random variable
callback(); //will print Window/global object
正如你所看到的, this
是调用函数时定义,而不是在它的声明,并取决于它如何被调用。
这正是setTimeout
内部或任何接收函数作为参数的函数中发生的事情:
/* fake */
function setTimeout(fnCallback, time) {
/* wait and when the time comes, call your callback like this: */
fnCallback(); //'this' will be Window/global
}
为了传递所需的上下文(在上面的示例中),我们可以强制使用上下文:
使用.bind
:
var callback = obj.key2.bind(obj); callback(); //will print obj
或使用.call
:
var callback = obj.key2; callback.call(obj); //will print obj
或者我们可以通过一个任意函数从内部调用我们的对象:
setTimeout(function() {
//here, 'this' is Window/global, because the anonymous function is being called from a callback assignment
obj.key2(); //will print obj
}, 3000);
因此,在您的示例中,为了正确设置setTimeout
回调并确保baz()
将接收类上下文,您可以:
将函数设置为回调时绑定函数:
setTimeout(this.baz.bind(this), 1000);
在您的类构造函数中, bind
一次baz
方法; 所以,每次调用它时,都会被分配类上下文。 像这样:
class Foo{ constructor() { this.baz = this.baz.bind(this) } bar(){ setTimeout(this.baz, 1000); } baz(){ console.log("this message should repeat roughly once per second"); this.bar(); } }
使用arrow functions
。 指定this
上下文的另一种方法是使用arrow functions
,实际上,确保this
赋值是通过词法范围完成的(不再是在函数调用中,而是在函数声明中)。
setTimeout(() => this.baz(), 1000); // ^^^^ // 'this' here is your class, will pass your class as 'this' // to the baz() method, due to the dot before
不同于:
setTimeout(function() { this.baz(); }, 1000); // ^^^^ // 'this' here is Window/global, will thrown undefined method
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.