我能否在 React 组件的构造函数中使用箭头函数?

17
这个问题与使用React时是优先使用箭头函数还是在构造函数中绑定函数?类似,但略有不同。你可以在构造函数中将函数绑定到this,或者只需在构造函数中应用箭头函数。请注意,在我的项目中我只能使用ES6语法。
class Test extends React.Component{
  constructor(props) {
    super(props);

    this.doSomeThing = this.doSomeThing.bind(this);
  }

  doSomething() {}
}
2.
class Test extends React.Component{
  constructor(props) {
    super(props);

    this.doSomeThing = () => {};
  }
}
这两种方法的优缺点是什么?谢谢。
4个回答

13

出于某些原因,通常情况下选项1更可取。

class Test extends React.Component{
  constructor(props) {
    super(props);

    this.doSomeThing = this.doSomeThing.bind(this);
  }

  doSomething() {}
}

原型方法更易于扩展。子类可以重写或扩展doSomething

doSomething() {
  super.doSomething();
  ...
}

实例属性

this.doSomeThing = () => {};

或 ES.next 类字段

doSomeThing = () => {}

如果使用代替方法,则无法调用super.doSomething(),因为该方法未在原型上定义。覆盖它将导致在父构造函数和子构造函数中分配this.doSomeThing属性两次。

原型方法也可以通过混合技术进行访问:

class Foo extends Bar {...}
Foo.prototype.doSomething = Test.prototype.doSomething;

原型方法更易于测试。在类实例化之前,它们可以被 spy、stub 或 mock。

spyOn(Foo.prototype, 'doSomething').and.callThrough();

这可以在某些情况下避免竞态条件。


谢谢您的回复!这个答案对我非常有帮助。您能否更详细地解释一下“某些情况下的竞态条件”呢?谢谢。 - ycdesu
例如,如果您想模拟 doSomething 或测试它是否被调用,但在实例上进行间谍/存根之前就已经被调用了。如果 doSomething 在构造函数中或代码的其他位置(当它是框架钩子等)中被调用,则可能会发生这种情况。 - Estus Flask
非常感谢您的解释。那帮助我找到了一个潜在的漏洞,谢谢。 - ycdesu

3

这不是有效的ES6。 - Elliot E
2
是的,但使用 bablerc,您可以在 stage-2 中使用插件,它会起作用。(transform-class-properties:http://babeljs.io/docs/plugins/transform-class-properties/)(preset-stage-2:http://babeljs.io/docs/plugins/preset-stage-2/) - Wei
谢谢。但是我们不能在项目中应用任何草稿语法 :( - ycdesu

3

对我来说,方法一更易读且更符合惯用法。

此外,将方法声明在类内而非构造函数中,可以使方法可共享。

class Foo {
  test() {}
}

const f1 = new Foo()
const f2 = new Foo()
f1.test === f2.test // true

在第二种方法中,每次创建新实例时,您将声明所有方法:
class Foo {
  constructor() {
    this.test = () => {}
  }
}

const f1 = new Foo()
const f2 = new Foo()
// the method is not shareable
f1.test === f2.test // false

理论上,方法2较慢,但对性能的影响应该可以忽略不计。 我会选择方法1,因为它在React documentation中被使用,而且我从未见过有人使用方法2。

我刚刚运行了一些样本来测试性能。在最新的Chrome(Mac)中,在构造函数中声明方法比在构造函数中使用bind要慢大约90%。


2
你的第一个代码示例在构造函数中包含 this.test = this.test.bind(this) 时不成立,就像@ycavatars发布的一样。f1.test === f2.test 将评估为false。 - Elliot E
当然不是,bind()总是返回一个新的方法。但是方法本身的声明可以共享。 - CodinCat
有没有使用 bind 都无所谓。bind 只是将方法包装起来。它应该比声明一个新方法更轻量和更快。 - CodinCat
@CodinCat,你能分享一下在jsperf上的性能测试吗?我想知道为什么使用bind比声明方法更快。谢谢。 - ycdesu
这个测试有意义吗?https://jsperf.com/bind-vs-arrow-function-in-constructor - ycdesu
显示剩余2条评论

1
检查一下这个:

https://babeljs.io/repl/#?babili=false&evaluate=true&lineWrap=false&presets=es2015%2Creact%2Cstage-2&targets=&browsers=&builtIns=false&debug=false&code=class%20Dog%20%7B%0A%20%20constructor()%20%7B%0A%20%20%20%20%0A%20%20%20%20this.cat%20%3D%20_%3D%3E%20%7B%0A%20%20%20%20%20%20this%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D

我们可以看到Babel进行了转译。
this.doSomeThing = () => { this };

转换为

var _this = this;
this.doSomething = function() { _this }

编辑:我稍微误读了你的帖子,但上面的内容仍然是正确且有趣的。@CodinCat指出重要的事情:在构造函数内声明函数意味着在创建对象时将花费时间(尽管很少)将该函数添加到对象中,并且还可能需要内存,因为该类的实例不共享相同的doSomeThing方法。

编辑2:将(this)绑定到函数实际上会导致我列出的确切问题。换句话说,这两种方法几乎完全相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接