原文連接:Demystifying Memory Usage using ES6 React Classesreact
做者: Donavon Westes6
如今已經有許多優秀的文章以不一樣的方式介紹使用 ES6 語法寫類方法。這些文章多數說起了此類方法的表現力(例如執行速度),但我並無看到其中有專一於內存影響的篇幅。bash
最近,這個話題在 Axel Rauschmayer 的推進下,被從新提起。對此,許多人表達了他們的觀點與想法,但顯而易見的是,多數人是隻知其一;不知其二的。app
I don't like this pattern: class C { handleClick = () => { ... } }函數
-@rauschmaui
這篇文章中,我不會探討執行速度的差別,我不會談論將 lambda 函數傳入組件會打斷 props 的淺比較,我也不會明確的建議你選擇哪兒一種方法來進行編碼。我只會羅列內存使用的相關事實,並幫助你作一個周到的決定。this
所以,讓咱們看一看兩種方案的簡單場景:在 constructor 中使用 bind,或者使用 class 屬性方法。編碼
我所指的類屬性方法是什麼呢,讓咱們看以下的示例:spa
class MyClass extends Component {
constructor() {
super();
this.state = { clicks: 0 };
}
handler = () => {
this.setState(({ clicks }) => ({ clicks: clicks + 1 }));
}
render() {
const { clicks } = this.state;
return(
<button onClick={this.handler}>
{`You've clicked me ${clicks} times`} </button> ); } } 複製代碼
這段示例使用了已經被承認的 ES 類屬性聲明語法,爲類的實例添加函數表達式。prototype
一樣,咱們使用 constructor bind 方式進行實現。這種語法的實現,在示例中顯得繁瑣,也須要更多時間閱讀代碼。
class MyClass extends Component {
constructor() {
super();
this.state = { clicks: 0 };
this.handler = this.handler.bind(this);
}
handler() {
this.setState(({ clicks }) => ({ clicks: clicks + 1 }));
}
render() {
const { clicks } = this.state;
return(
<button onClick={this.handler}>
{`You've clicked me ${clicks} times`} </button> ); } } 複製代碼
在咱們分析第一個實例中 constructor 方法如何執行前,讓咱們確認一下 ES6 的類方法具體作了什麼。回想過去的日子(一兩年前),你是如何在 ES6 類以前編寫這些代碼的?你可能會這麼寫:
MyClass.prototype.handler = function handler() {
...
};
複製代碼
這就是當你建立類方法時,ES6 語法糖爲你作的。那麼,constructor 函數在 ES5 中是如何表現的呢?
function MyClass() {
this.handler = this.handler.bind(this);
}
複製代碼
是否與你腦海中想的同樣呢,接下來看一看 MDN 對於 Function.prototype.bind()
方法的說明:
方法建立一個新的函數, 當被調用時,將其this關鍵字設置爲提供的值,在調用新函數時,在任何提供以前提供一個給定的參數序列。
所以,當咱們調用實例屬性中的 handler(包含一個指向匿名函數的指針),在 constructor 函數中的 bind 方法被調用,而 bind 方法綁定了實例的 this 並調用原型函數。
就這個實例而言,所花費的內存代價很小,僅僅包含指向匿名函數的函數指針,而方法自己處於原型對象上。
兩個方法的行爲相同,他們的內存足跡又會怎麼變現呢?
我繪製了以下的圖表幫助我說明各方案的內存足跡。紅色區域表明類,綠色區域表明實例,實線框表明內存的使用,虛線框表示從類中繼承的方法。就內存使用量而言,繼承方法遠小於實例方法。
首先,咱們看一下類屬性方法的表現(即便用了箭頭函數的handler
)
注意到基礎的 MyClass
只包含了 render
方法,其餘全部的內存消耗,來源於每個實例(實線盒子),每個實例不只包含 state
、指向 render
的指針,還包含了 handler
方法。當你僅僅建立幾個實例時,或許不是個大問題。
如今,讓咱們看一下 bind 方法的表現。
這裏,基礎的 MyClass
包含了 render
方法以及 handler
方法,這一次,每個實例只包含 state
以及體積很小、用於調用 handler
方法的匿名函數。每個示例的內存足跡要更小。
總的來講,只有當你對同一個類建立大量的實例時,這部分節約的內存會表現的很好,例如一個列表項。
當內存消耗不多時,使用 constructor bind 方法並非那麼方便。考慮使用單例的場景,內存的結餘或許還不值得編碼的複雜性提高。
在多數狀況下,兩種方式沒有過多的差別。考慮到你已經理解了二者間的差別,在具體場景中正確決策並非難事。
我的而言,我喜歡類屬性的語法,然而最佳實踐將會是 IMO 推出一個 Babel 將類屬性方法轉換成原型方法。若是你知道這樣的 Babel,或者渴望本身實現,請聯繫我。
請記住:計算機很是擅長閱讀代碼,你無需擔憂。當考慮到讓本身的代碼可讀性(對人類)更強,使用箭頭函數則更優。
譯者:Robottdog.C
Blog:robottdog.com