1. 程式人生 > >為什麼React事件處理函式必須使用Function.bind()繫結this?

為什麼React事件處理函式必須使用Function.bind()繫結this?

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。對於這個做法,官網給出的註釋是:

這段話說了看似說了很多,其實就兩點:

1.如果你不繫結this.handleClick方法,那麼在事件發生並且精確呼叫這個方法時,方法內部的this會丟失指向。
2.這不是React的原因,這是JavaScript中本來就有的。如果你傳遞一個函式名給一個變數,然後通過在變數後加括號()來呼叫這個方法,
 此時方法內部的this的指向就會丟失

這一段點明瞭為什麼要在建構函式中繫結this,因為JavaScript中確實有這麼一個陷阱。具體是怎麼樣的呢?我進行了一下測試:

按 Ctrl+C 複製程式碼

按 Ctrl+C 複製程式碼

為了便於為學習ES6的童鞋理解以及說明這是JavaScript中的陷阱而非React所特有,這裡使用字面量表達式宣告物件。

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

現在修改一下程式碼:

按 Ctrl+C 複製程式碼

按 Ctrl+C 複製程式碼

注意到現在沒有直接呼叫obj物件中的testLog方法,而是使用了一箇中間變數tmpLog過渡,當使用括號()呼叫該方法時,方法中的this丟失了指向,會指向window,進而window.tmp未定義就是undefined:

說了這麼多,跟React事件處理函式的繫結有什麼關係呢?

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

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

這樣說,React中的事件名(上圖中的onClick)就是我所舉例子中的中間變數,React在事件發生時呼叫onClick,由於onClick只是中間變數,所以處理函式中的this指向會丟失,為了解決這個問題,我們需要在例項化物件的時候,需要在建構函式中繫結this,使得無論事件處理函式如何傳遞,它的this的指向都是固定的,固定指向我們所例項化的物件。