精讀:為什麼我們要寫super(props)

破衣丁發表於2019-01-15

在寫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…

相關文章