簡體   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