在寫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 會自動被引用。