精读:为什么我们要写super(props)

在写React时,我们经常会写下面这样的组件:

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
  }
  // ...
}
复制代码

有几个问题需要值得我们思考:

为什么我们要调用 super ? 我们能不调用 super 吗? 如果我们必须要调用它,不传 props 会发生什么?它们还有其它参数吗?

在JavaScript中,super 指向了父级class 的 constructor,(例子中就是 React.Component的原型对象)。 你不能使用 this 在constructor中的 super 调用之前:

class Checkbox extends React.Component {
  constructor(props) {
    // 这里不能使用this
    super(props);
    // 现在这里可以使用this
    this.state = { isOn: true };
  }
  // ...
}
复制代码

为什么 JavaScript 强制要求我们调用 super, 在 constructor 中使用 this 前:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class PolitePerson extends Person {
  constructor(name) {
    this.greetColleagues(); // 这里不允许我们使用this,下面解释
    super(name);
  }
  greetColleagues() {
    alert('Good morning folks!');
  }
}
复制代码

上面的例子假设调用 super 之前允许使用 this, 一段时间后,我们在 greetColleagues( ) 中添加:

  greetColleagues() {
    alert('Good morning folks!');
    alert('My name is ' + this.name + ', nice to meet you!');
  }
复制代码

但是我们忘了,this.greetColleagues( ) 在 super 调用之前,this.name 都没有定义,代码会抛错,像这样的代码可能很难想到什么时候发生。

因此,为了避免这个陷阱,JavaScript 强制要求在 constructor 中使用 this 之前,必须先调用 super。 这个限制同样也适用在 React Component 中,当然 class fidlds propsal 让我们也可以通过一些方式来简写,不用这么麻烦写constructor( ):

class Checkbox extends React.Component {
  state = { isOn: true };
  // ...
}
复制代码

为什么要传递props?

你可能会想,传递 props 给 super 是有必要的,这样它继承的 React.Component 的 constructor 就能初始化this.props:

// 在React 内部
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}
复制代码

但它并不仅仅是为了初始化 this.props, 从它的源码里解释看可以更新Component 状态。实际上即使你调用super() 不传props, 你仍然能访问 this.props 在 render 或者其他方法,这是因为在调用 constructor 后 React 能正确的分配 props 在实例对象上。

  // React 内部
  const instance = new YourComponent(props);
  instance.props = props;
复制代码

因此,即使你忘记传 props 参数给 super( ), React 也会设置 props 能使你访问。但是不是意味着我们就不需要再传 props 给 super?

并不是,因为这样容易因为混乱,尽管你的实例对象执行 constructor 后 React 会分配this.props ,但 this.props 仍是 undefined。

// react 内部
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}

// 你的代码里
class Button extends React.Component {
  constructor(props) {
    super(); // 我们不传 props
    console.log(props);      // {}
    console.log(this.props); // undefined 
  }
  // ...
}
复制代码

这会给我们 debug 带来很大麻烦,React 还是推荐我们将props 传递 super:

class Button extends React.Component {
  constructor(props) {
    super(props); // 我们传递 props
    console.log(props);      //  {}
    console.log(this.props); //  {}
  }
  // ...
}
复制代码

这样就确保 this.props 被设置在执行 constructor。

在 React 16.6 增加了context 在constructor 第二个参数,为什么 我们不写 super(props, context )? 实际上你可以这样写,但实际上 context 用的比较少,所以没有这样传。

最后,随着 class fidlds propsal 这个tc39提案引入,上面的这些问题大部分都不会存在。没有 constructor , 所有的参数都会自动传递过来,就像 state = { } 表达式这样写法,this.props 和 this.context 会自动被引用。

引用: overreacted.io/why-do-we-w…

猜你喜欢

转载自juejin.im/post/5c3e05a751882525a27fd537