2020年9月4日 星期五

[html, javascript] arrow function, bind, this, that? 處理 Callback method 中存取 class members 的常見基礎問題.

處理 Callback method 中存取 class members 的常見基礎問題.

先說解決方法: 使用 arrow function, 把 callback method 的 this 與呼叫的 this 綁起來.

class MyClass{

   initUI(){

        let button = document.createElement('button');

        button.innerHTML = 'ok'; 

        const that = this;

        button.addEventListener('click', => () {

                // this.constructor.name => MyClass

                console.log('this.constructor.name = ', this.constructor.name);        

                this.myFun(); // <--- OK

        });

   }

        

    myFun(){

         console.log('myFun()');

    }

} // end of class


Enjoy! 

by Jing.


細節:

主要是因為一個按鈕的 addEventListener 參數可以是物件也可以是一個 javascript method. 物件可以容易理解, 但如果呼叫的是 method, 那這個 method 裡面的 this reference 確定不是你原來的物件. 

出問題的程式碼

class MyClass{

   initUI(){

        let button = document.createElement('button');

        button.innerHTML = 'ok'; 

        

        button.addEventListener('click', function () {

                // this => HTMLButtonElement

                console.log('this.constructor.name = ', this.constructor.name); 

    

                this.myFun(); // <--- Error: undefined

        } );

   }

        

    myFun(){

         console.log('myFun()');

    }

} // end of class



所以有人用  that 先把 this 存起來, 然後在 callback method 裡面存取

使用 that 解決問題的版本

class MyClass{

   initUI(){

        let button = document.createElement('button');

        button.innerHTML = 'ok'; 

        const that = this;

        button.addEventListener('click', function () {

                // this => HTMLButtonElement

                console.log('this.constructor.name = ', this.constructor.name); 

                // that => MyClass

                console.log('that.constructor.name = ', that.constructor.name);

                that.myFun(); // <--- OK

        } );

   }

        

    myFun(){

         console.log('myFun()');

    }

} // end of class


如果不想多一個變數, 也可以使用 bind method, 明確指定 callback method 裡面的 this 是外面的 this

使用 bind with inline anonymous  method 解決問題的版本

class MyClass{

   initUI(){

        let button = document.createElement('button');

        button.innerHTML = 'ok'; 

        const that = this;

        button.addEventListener('click', function () {

                // this.constructor.name => MyClass

                console.log('this.constructor.name = ', this.constructor.name);        

                this.myFun(); // <--- OK

        }.bind(this) );

   }

        

    myFun(){

         console.log('myFun()');

    }

} // end of class



最優雅的方式是使用 arrow function, 直接挑明了這個 method 的 this 是跟 parent 一樣.

class MyClass{

   initUI(){

        let button = document.createElement('button');

        button.innerHTML = 'ok'; 

        const that = this;

        button.addEventListener('click', => () {

                // this.constructor.name => MyClass

                console.log('this.constructor.name = ', this.constructor.name);        

                this.myFun(); // <--- OK

        });

   }

        

    myFun(){

         console.log('myFun()');

    }

} // end of class