115

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

 6 years ago
source link: https://zhuanlan.zhihu.com/p/32831853?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

[阅 #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 文档——https://babeljs.io/docs/plugins/transform-class-properties/,那么也就说,每个实例都对 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 已经放出了第五季的预告片(自备梯子)——https://www.youtube.com/watch?v=lRs72x7Lgtc,有兴趣的同学如果还没看过前面四季的话不妨去补一下,它每季之间剧情还是有一定关联的。

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK