前言
使用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的方法,每个实例都可以有效地共享函数体,从而更加有效的使用内存。但当要绑定的函数较多时,这种方法又显得相对的枯燥和无聊。所以,我认为在知道实例不多,函数体不大的前提下,使用箭头函数更加快捷。