前言
使用ES的class,到底是使用箭頭函式更有效率,還是在構造器函式(constructor)中使用bind函式給函式繫結context更加有效率?在Demystifying Memory Usage using ES6 React Classes文章中,給出瞭如下的結論。
上圖是在class中使用箭頭函式建立類的方法。作者稱其為class properties. 可以看得出,使用箭頭函式在類中定義的handler方法,在其的每一個例項中都會有一個獨立的handler函式。當例項的數量不多的時候,這種方式不會帶來很大的記憶體浪費,但當例項成千上萬的時候,這種方式就會顯得在記憶體使用上效率很低。若是能夠共享類上的handler豈不是更好。
而使用構造器函式繫結,則能夠更加有效的減少記憶體的使用。通過在構造器函式裡面繫結函式,如下程式碼:
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>
);
}
}
複製程式碼
每一個例項裡面雖然都有handler方法(this.handler,不是MyClass.prototype.handler),但每個例項中的this.handler實際上都共享MyClass.prototype.handler的函式體。
那麼這是如何做到的呢? Bind函式MDN Polyfill實現:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) { //oThis 代表object this
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this, //
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
// 獲取呼叫時(fBound)的傳參.bind 返回的函式入參往往是這麼傳遞的
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 維護原型關係
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
}
複製程式碼
從上面的程式碼可以看出,我們在構造器中使用bind繫結函式以後,實際上返回了一個函式(fBound),所返回的函式的函式體和待繫結(MyClass.prototype.handler)的函式體是不同的。所返回的函式只是在函式體內呼叫了待繫結的函式。換句話說,通過在構造器中使用bind繫結函式以後,所生成的例項中,都會有一個獨立的fBound,函式體相對於待繫結的函式較小,而每個例項中的fBound在被呼叫時都會共享待繫結的函式(MyClass.prototype.handler)
結論
通過對比可以發現,使用構造器bind的方法,每個例項都可以有效地共享函式體,從而更加有效的使用記憶體。但當要繫結的函式較多時,這種方法又顯得相對的枯燥和無聊。所以,我認為在知道例項不多,函式體不大的前提下,使用箭頭函式更加快捷。