繁体   English   中英

在 `setTimeout` 中使用成员函数时,将 babel 类保留为 `this`

[英]Keep babel class as `this` when using a member function in `setTimeout`

我有一个 ES2015 类,称为Foo ,它至少有两个成员函数barbaz 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
}

“解决方法”:

为了传递所需的上下文(在上面的示例中),我们可以强制使用上下文:

  1. 使用.bind

     var callback = obj.key2.bind(obj); callback(); //will print obj
  2. 或使用.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()将接收类上下文,您可以:

  1. 将函数设置为回调时绑定函数:

     setTimeout(this.baz.bind(this), 1000);
  2. 在您的类构造函数中, 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(); } }
  3. 使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM