在ES6引入class之前,JavaScript使用建構函式和原型來模擬類別繼承。後來有class語法後,提供了更簡潔、可讀性更高的方式。
先介紹先前的寫法~
過去兩個建構函式要共用屬性與方法,需要以下步驟才能:
範例:
function Animal(breed){
this.breed = breed;
}
function Dog(name,breed){
this.name = name;
Animal.call(this,breed)
}
先建立兩個建構函式Animal和Dog,他們彼此沒有任何關聯。
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
現在要讓Dog繼承Animal。先前有說到,Object.create()
它會建立一個新的空物件,而空物件的原型繼承指定傳入的物件。
所以讓Dog.prototype
繼承Animal.prototype
,但這樣會讓Dog
上的prototype屬性的 constructor
會不見,如圖:
constructor
他原本預設要指回Dog
函式。所以再把他指回去Dog
函式,也就是Dog.prototype.constructor = Dog
。
執行結果:
看到Dog的prototype裡面的constructor
回來了~
Animal.prototype.sleep = function(){
console.log("睡覺zzz");
}
Dog.prototype.bark = function(){
console.log("汪!");
}
const puppy = new Dog("點點","chiwawa");
為了方便測試,在Animal的prototype上加上了sleep方法,Dog的prototype加上bark方法,再用new Dog建立實例puppy
。
puppy.sleep();
puppy.bark();
執行puppy.sleep()和puppy.bark(),看看原型的方法能不能使用。
執行結果:
puppy.sleep()和puppy.bark()皆執行成功~
console.log(puppy);
//Dog {name: '點點', breed: 'chiwawa'}
再來印出puppy看看,確認有拿到Animal的breed屬性。
以上就是傳統寫法~
接下來改用class語法糖,幫助我們程式碼更加簡潔,不用修改原型物件,也可以有繼承。
語法
class name {
// class body
}
class name extends otherName {
// class body
}
把剛剛的題目改成class寫法:
//定義前代類別Animal
class Animal {
constructor(breed) {
this.breed = breed;
}
sleep() {
console.log("睡覺zzz"); //定義睡覺方法
}
}
//定義後代類別Dog,並繼承Animal
class Dog extends Animal {
constructor(breed, name) {
super(breed); //呼叫前代類別的constructor
this.name = name; //後代類別的屬性
}
bark() {
console.log("汪!"); //定義叫聲方法
}
}
const puppy = new Dog("chiwawa", "點點");
裡面有幾個關鍵字需要知道:
extends
:class只需使用extends
關鍵字就能繼承。所以class Dog extends Animal
也就是Dog繼承自Animal,很直觀的寫法。constructor()
:放原本建構函式的邏輯。例如:Animal建構函式原本的this.breed = breed
就放在這裡面。super()
:是前代類別的 constructor。在後代類別的 constructor中呼叫 super() 就能繼承前代類別的屬性。puppy.sleep();
puppy.bark();
//"睡覺zzz"
//"汪!"
sleep()方法定義在Animal上,bark()方法定義在Dog的原型上,所有Dog的實例都可以呼叫這兩種方法。
console.log(Animal.prototype.sleep);
//sleep(){console.log("睡覺zzz");}
console.log(Dog.prototype.bark);
//bark(){console.log("汪!");}
用console.log 檢查 sleep() 和 bark() 是否定義在 prototype上,結果正確。
console.log(puppy);
//Dog {breed: 'chiwawa', name: '點點'}
最後確認印出puppy,跟傳統寫法結果一樣~
new
關鍵字使用,忘記加會報錯。const dog1 = Dog("小黑","Formosan Mountain Dog");
//TypeError: Class constructor Dog cannot be invoked without 'new'
class Dog extends Animal {
constructor(breed, name) {
this.name = name; //後代類別的屬性
super(breed); //呼叫前代類別
}
//ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
以上分享~謝謝!
MDN - class
JS 原力覺醒 Day23 - Class 語法糖