Why Do We Write super(props)
Redux 作者 Dan Abramov 发在博客的一篇文章。
早上逛掘金发现已经有译文:
【译】为何我们要写super(props)?
想想要总结下写下读后感,才算没白读。
总结几点印象比较深刻的:
- 为什么要在使用
this
前调用super()
?
考虑这样的场景:
class Person {
constructor(name) {
this.name = name;
}
}
class PolitePerson extends Person {
constructor(name) {
this.greetColleagues(); // 🔴 This is disallowed, read below why
super(name);
}
greetColleagues() {
alert('Good morning folks!');
}
}
假设我们允许在 super()
调用前调用 this
,我们在子类调用了继承自父类的方法,这样做似乎没什么问题。一个月后当我们想要更改我们的代码:
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}
因为我们在调用 super
前调用了 this.greetColleagues
,而 this.name
需要在调用了 super
之后才被实例化,所以此时 this.name
将是未定义的!
为了避免这样的情况发生,JS将 super
必须在执行 this
方法之前执行 作为强制规定。
- 为什么要在
super()
中传入 props?
有些人认为只有给super
传入 props ,React 才会帮你初始化props
。
这个答案很接近,实际上,即使你不在super()
里传入props
,一样可以在render
函数里拿到this.props
,因为 React 已经帮你做了:
// Inside React
const instance = new YourComponent(props);
instance.props = props;
真正要传入 props
的原因是,如果不传入,你将无法在 super() 到 constructor
结束这个区间内拿到 this.props
, this.props
将是 undefined
:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// Inside your code
class Button extends React.Component {
constructor(props) {
super(); // 😬 We forgot to pass props
console.log(props); // ✅ {}
console.log(this.props); // 😬 undefined
}
// ...
}
所以虽然给 super()
传入 props
不是必须的,但是为了避免出现不必要的 bug,建议总是传入 props
为好。
- state 不一定要传入
constructor
中
因为有了 class fields proposal(似乎是个新东西,要研究一下),所以我们不需要声明constructor
即可声明state
:
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
结合我的实践
想想以前工作的 时候如果声明了 constructor
,我都不会传入 props
,甚至因为 ts 的检查,为了让 super
有值,我传入了 undefined
...所以传入 props
才是最佳实践。
还有最后的 state 声明方法,之前接了组里同事的代码后发现她就是这么写的,我竟然还想把他们改成 constructor
内声明,孤陋寡闻了...
关于 class fields proposal
看了下大概是对类内属性的声明发生了改变,在 ES6 中,类的属性必须在 constructor
内声明,但新的方案让类的属性也可以直接在类的内部声明即可:
// ES6
class Counter extends HTMLElement {
clicked() {
this.x++;
window.requestAnimationFrame(this.render.bind(this));
}
constructor() {
super();
this.onclick = this.clicked.bind(this);
this.x = 0;
}
connectedCallback() { this.render(); }
render() {
this.textContent = this.x.toString();
}
}
window.customElements.define('num-counter', Counter);
// Field declarations
class Counter extends HTMLElement {
x = 0;
clicked() {
this.x++;
window.requestAnimationFrame(this.render.bind(this));
}
constructor() {
super();
this.onclick = this.clicked.bind(this);
}
connectedCallback() { this.render(); }
render() {
this.textContent = this.x.toString();
}
}
window.customElements.define('num-counter', Counter);
看到上面的示例我以为如果在 contructor
外声明的方法必须在 constructor 内做 this
绑定,但是事实上在 constructor
外声明的方法, this 也是指向实例的。
网友评论