爲何React事件處理函數必須使用Function.bind()綁定this?

最近在React官網學習Handling Events這一章時,有一處不是很明白。代碼以下:html

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

注意到在Toggle類的構造函數constructor類中,有一句註釋:「This binding is necessary to make `this` work in the callback」,即在構造函數中,利用Function.bind()函數將類中已有的handleClick函數再次綁定了一下this。對於這個作法,官網給出的註釋是:react

這段話說了看似說了不少,其實就兩點:es6

1.若是你不綁定this.handleClick方法,那麼在事件發生而且精確調用這個方法時,方法內部的this會丟失指向。
2.這不是React的緣由,這是JavaScript中原本就有的。若是你傳遞一個函數名給一個變量,而後經過在變量後加括號()來調用這個方法,
 此時方法內部的this的指向就會丟失

這一段點明瞭爲何要在構造函數中綁定this,由於JavaScript中確實有這麼一個陷阱。具體是怎麼樣的呢?我進行了一下測試:函數

let obj = {
    tmp:'Yes!',
    testLog:function(){
        console.log(this.tmp);
    }
};
obj.testLog();

爲了便於爲學習ES6的童鞋理解以及說明這是JavaScript中的陷阱而非React所特有,這裏使用字面量表達式聲明對象。學習

通過測試,這樣使用obj中的testLog方法時,this指向obj,可以正常輸出tmp屬性:測試

如今修改一下代碼:this

let obj = {
    tmp:'Yes!',
    testLog:function(){
        console.log(this.tmp);
    }
};
let tmpLog = obj.testLog;
tmpLog();

注意到如今沒有直接調用obj對象中的testLog方法,而是使用了一箇中間變量tmpLog過渡,當使用括號()調用該方法時,方法中的this丟失了指向,會指向window,進而window.tmp未定義就是undefined:spa

說了這麼多,跟React事件處理函數的綁定有什麼關係呢?code

前面講過,React跟原生JavaScript的事件綁定區別有兩點,其中第二點就是:htm

即在React(或者說JSX)中,傳遞的事件參數不是一個字符串,而是一個實實在在的函數:

這樣說,React中的事件名(上圖中的onClick)就是我所舉例子中的中間變量,React在事件發生時調用onClick,因爲onClick只是中間變量,因此處理函數中的this指向會丟失,爲了解決這個問題,咱們須要在實例化對象的時候,須要在構造函數中綁定this,使得不管事件處理函數如何傳遞,它的this的指向都是固定的,固定指向咱們所實例化的對象。

 

這個JavaScript陷阱是對我以前文章關於JavaScript中的this的一個很好的補充。

 

另外,在後續學習當中,發現可能存在其餘緣由,具體參見阮一峯的博客:ES6 class語法中this的指向

相關文章
相關標籤/搜索