[英]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.