在class
中具有繼承的特性,基本概念是允許一個class
擴展別的class
的內容。我們可以將這個被擴展的class稱為「父類」(parent),而進行擴展的class稱為「子類」(child)。
基本語法就會是先寫一個新的子類,再從後面加上extends
繼承需要的父類:
class ChildClass extends ParentClass { /* … */ }
父類需要是具有可以使用new
跟prototype
的constructor function
。
當我們使用extends
,需要使用super關鍵字查找屬性或者函式。
前幾天的範例都使用煮火鍋比賽的參賽者,這次我們先從比賽格式開始建立起,當有比賽的訊息要發布時,可以使用Competition
填入比賽主題與日期:
class Competition{
constructor(competitionName,date){
this.competitionName = competitionName;
this.competitionDate = date;
}
competitionNews(){
return `The ${this.competitionName} competition will be held on ${this.competitionDate}.`;
}
}
接著擴展參賽者的class,讓參賽者的格式中也包含比賽資訊,構成一段完整的介紹活動文:
class Contestant extends Competition {
constructor(name,date,contestantName){
super(name,date)
this.contestantName = contestantName;
}
sayName(){
return `My name is ${this.contestantName}.`;
}
}
const contestant1 = new Contestant('hot pot','October 40th','Alice')
console.log(contestant1.sayName());
console.log(contestant1.competitionNews());//My name is Alice.
//The hot pot competition will be held on October 40th.
//也可以使用super取用父類的方法
class Contestant extends Competition {
constructor(name,date,contestantName){
super(name,date)
this.contestantName = contestantName;
}
sayName(){
//將父類方法直接放在想使用的方法裡
const competitionInfo = super.competitionNews();
return `My name is ${this.contestantName}. ${competitionInfo}`;
}
}
const contestant1 = new Contestant('hot pot','October 40th','Alice')
//僅使用一次方法
console.log(contestant1.sayName());
既然有說只要是建構函式都能擴展子類的話,來試著一開始用基本的建構函式擴展:
function Competition(competitionName,date){
this.competitionName = competitionName;
this.competitionDate = date;
this.competitionNews = function(){
return `The ${this.competitionName} competition will be held on ${this.competitionDate}.`;
}
}
class Contestant extends Competition {
constructor(competitionName,date,contestantName){
super(competitionName,date)
this.contestantName = contestantName;
}
sayName(){
const competitionInfo = super.competitionNews();
return `My name is ${this.contestantName}. ${competitionInfo}`;
}
}
const contestant1 = new Contestant('hot pot','October 40th','Alice')
console.log(contestant1.sayName());
//ReferenceError: competitionNews is not defined
假如寫出基本的建構函式直接拿去給子類擴展,會發現這是有問題的,猜想因為class在傳遞方法的時候,是透過class的prototype在傳遞的。
如果記得昨天提到class的prototype,會發現prototype的預設上是把建構子跟其他方法都被封裝在裡面,如果想使用原本的建構函式傳遞方法,就需要把方法寫在prototype。
讓我們修改寫法,讓Contestant
能被正常擴展:
function Competition(competitionName,date){
this.competitionName = competitionName;
this.competitionDate = date;
}
//把method寫在prototype
Competition.prototype.competitionNews = function(){
return `The ${this.competitionName} competition will be held on ${this.competitionDate}.`;
}
class Contestant extends Competition {
constructor(competitionName,date,contestantName){
super(competitionName,date,contestantName)
this.contestantName = contestantName;
}
sayName(){
return `My name is ${this.contestantName}. ${super.competitionNews()}`;
}
}
const contestant1 = new Contestant('hot pot','October 40th','Alice')
console.log(contestant1.sayName());//My name is Alice. The hot pot competition will be held on October 40th.
如果子類繼承父類,就一定要在子類constructor()中必須使用super(),且必須在this
之前使用,目前觀察的結果,父類即使沒有constructor()設定property,子類只要有constructor()存在,就會要求一定要使用super()。子類需要透過super()執行父類的constructor()去初始化屬性。
class Competition{
constructor(competitionName,date){
this.competitionName = competitionName;
this.competitionDate = date;
}
competitionNews(){
return `The ${this.competitionName} competition will be held on ${this.competitionDate}.`;
}
}
//把Competition相關的內容都刪掉
class Contestant extends Competition {
constructor(contestantName){
this.contestantName = contestantName;
}
sayName(){
return `My name is ${this.contestantName}.}`;
}
}
const contestant1 = new Contestant('Alice')
console.log(contestant1.sayName());//ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
How to extend a class without having to use super in ES6?
Class inheritance