在 javascript 語言中,關於 this 這個關鍵字的行為一直以來困擾著一代又一代初級開發者。同時 this,也充分反應了 javascript 的詭異與靈活。
但是請別誤會,這篇文章並不會對 this 的特徵進行全方位講解,因為這些內容都可以在各種前端書籍中找到答案。這裡,我試圖結合 React 事件處理函數關於 this 綁定的演化史,談一談這個框架設計以及 javascript 語言在這一細節上的進步和完善。同時對比 this 綁定的不同方案,讓大家對 React 、ES next 有一個更清晰的認識。
- React.createClass 自動綁定;
- 渲染時綁定;
- 箭頭函數綁定;
- Constructor 內綁定;
- Class 屬性中使用 = 和箭頭函數
方法一:React.createClass 自動綁定
React 中創建組件的方式已經很多,比較古老的諸如 React.createClass 應該很多人並不陌生。當然,從 React 0.13 開始,可以使用 ES6 Class 代替 React.createClass 了,這應該是今後推薦的方法。 但是需要知道,React.createClass 創建的組件,可以自動綁定 this。也就是說,this 這個關鍵字會自動綁定在組件實例上面。
// This magically works with React.createClass
// because `this` is bound for you.
onChange = {this.handleChange}
當然很遺憾,對於組件的創建,官方已經推薦使用 class 聲明組件或使用 functional 無狀態組件
方法二:渲染時綁定
通過前文,我們知道最傳統的組件創建方式不會有 this 綁定的困擾。接下來,我們假定所有的組件都採取 ES6 classes 方式聲明。這種情況下,this 無法自動綁定。一個常見的解決方案便是:
onChange = {this.handleChange.bind(this)}
這種方法簡明扼要,但是有一個潛在的性能問題:當組件每次重新渲染時,都會有一個新的函數創建。OMG! 這聽上去貌似是一個很大的問題,但是其實在真正的開發場景中,由此引發的性能問題往往不值一提(除非是大型組件消費類應用或遊戲)。
方法三:箭頭函數綁定
這種方法其實和第二種類似,拜 ES6 箭頭函數所賜,我們可以隱式綁定 this
onChange = {e => this.handleChange(e)}
當然,也與第二種方法一樣,它同樣存在潛在的性能問題。下面將要介紹的兩種方法,可以有效規避不必要的性能消耗,請繼續閱讀。
方法四:Constructor 內綁定
constructor 方法是類的默認方法,通過new命令生成對象實例時,自動調用該方法。 所以我們可以:
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
這種方式往往被推薦為“最佳實踐”,也是筆者最為常用的方法。 但是就個人習慣而言,我認為與前兩種方法相比,constructor 內綁定在可讀性和可維護性上也許有些欠缺。 同時,我們知道在 constructor 聲明的方法不會存在實例的原型上,而屬於實例本身的方法。每個實例都有同樣一個 handleChange,這本身也是一種重複和浪費。 如果你對 ES next 一直抱有開放的思想,且能夠使用 stage-2 的特性,不妨嘗試一下最後一種方案。
方法五:Class 屬性中使用 = 和箭頭函數
handleChange = () => {
// call this function from render
// and this.whatever in here works fine.
};
我們來總結一下這種方式的優點:
【1】使用箭頭函數,有效綁定了 this;
【2】沒有第二種方法和第三種方法的潛在性能問題;
【3】避免了方法四的組件實例重複問題;
【4】 我們可以直接從 ES5 createClass 重構得來。
總結
本文在對比 React 綁定 this 的五種方法的同時,也由遠及近了解了 javascript 語言的發展:從 ES5 的 bind, 到 ES6 的箭頭函數,再到 ES next 對 class 的改進。 React 作為蓬勃發展的框架也同樣在與時具進,不斷完善,結合語言特性的發展不斷調整著自身。 最後,我們通過這張圖片來完整回顧:
閱讀更多 JS加加網 的文章
關鍵字: 語言 一代 JavaScript