原文连接: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