首发于jscourse
[阅 #36] 在写 React 代码时,用类属性和在构造器中用 bind 哪个内存使用更少

[阅 #36] 在写 React 代码时,用类属性和在构造器中用 bind 哪个内存使用更少

「阅」——JSCourse 旗下栏目,专门推荐我们为大家精心挑选的优质 JavaScript 相关技术内容

在写 React 代码的时候,Render 函数中,往往需要调用当前组件实例中定义的函数,这个时候我们需要解决被调函数中 this 的指向问题,一般我们都会采取三个方案:

  • 用类属性
  • 用 bind
  • 用 inline function

inline function 就像这样:

class MyComponent extends React.Component {
  render() {
    const msg = "Hello " + this.props.user.name.first;
    return <PureChild onClick={() => this.props.onAlert(msg)} />;
  }
}

今天不讨论 inline function,下期会介绍这部分。今天主要讨论前两者,因为小编最近读到了一篇 Donavon West 的文章——Demystifying Memory Usage using ES6 React Classes: https://medium.com/dailyjs/demystifying-memory-usage-using-es6-react-classes-d9d904bc4557(记得梯子),主要讲解了前两者在内存使用上的情况。

我们先来看看使用类属性是怎么样的:

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>
    );
  }
}

上述代码相信绝大部分写过 React 的同学都能看懂,重点在 handler 属性这里,它将一个箭头函数表达式赋值给了 handler 属性。这里其实用了 ES 新特性——类属性,由于浏览器大多尚未支持该特性,一般我们会用 babel 进行 transform,而 babel 在对类属性进行 transform 的时候,这里的 handler 是直接绑定在实例上的,你可以直接用 babel 转下看看就知道了,而具体原因可以看 babel 文档——babeljs.io/docs/plugins,那么也就说,每个实例都对 handler 属性会有较大的内存开销。

我们再来看看在构造函数中直接 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>
    );
  }
}

这里就用了 bind 来修正 this 的指向,这种和上一种的区别就是,这种情况下每个实例中的 handler 只保存了对其基类中 handler 函数的一种引用,最终是会去调用基类中的 handler 函数的。等效的 ES5 代码差不多这样:

function MyClass() {
  this.handler = this.handler.bind(this);
}

MyClass.prototype.handler = function handler () {...}

这种情况下,每个实例对 handler 的内存开销就会小很多。下面是两种情况下,内存使用大致示意图,其中实线的框表示内存开销相对略大,虚线则表示相对比较小。

当然了,分享这个只是让大家对这方面的知识知晓下,实际上一方面只有当你有大量实例创建出来的时候,这种内存的开销差异才会体现得比较明显(比如:ListView),另外一方面,尽管类属性的写法理论上内存开销相对较大,但是写起来很方便,而且可读性也很好,所以,绝大部分情况下,除非这点性能差异对你的应用而言非常重要,否则小编觉得还是应该以可读写和书写便利性为先,再者 babel 自身的 transform 也有优化的可能,这部分的内存开小差异也可能会被尽可能地缩小甚至避免。

最后分享一则娱乐消息:美剧《硅谷》第五季将于 3月12日 开播,HBO 已经放出了第五季的预告片(自备梯子)——youtube.com/watch?,有兴趣的同学如果还没看过前面四季的话不妨去补一下,它每季之间剧情还是有一定关联的。

好了,以上就是本期内容,我们下期再见咯,提前祝大家周末愉快!


关注「jscourse」微信公众号获取更多 JavaScript 学习课程和资料!

发布于 2018-01-12 07:46