ES6新增了Class的語法,這個語法糖讓我們用更簡化的程式碼寫出原型鏈。但要注意,雖然我們寫Class語法,但跟之前寫建構函式一樣,背後原理一樣都是操作物件的prototype,得出的結果也是一樣,這一點是沒變的。縱使我們寫Class,JavaScript仍然是一個Prototype-based語言,並非一個完全的物件導向語言。
用Class代替建構函式的好處:
.call
、Object.create()
去做繼承constructor
屬性,我們需要手動補回來,但用Class語法就不用補回class
的方法裏面,方法就寫在最外層。避免開發者不經意把屬性、變數等資料,一併共用給下層從最簡單的例子說起,以前會先建立建構函式,並在它的原型裏定義想共享給下層的方法,最後再new
出一個實體物件。
function Car(type,color,person){
this.type = type;
this.color = color;
this.person = person;
}
//在建構函式Car的原型物件裏,定義會共享的方法
Car.prototype.carHorn = function(){
console.log(`${this.type}發出車輛的嗚響` )
}
const myCar = new Car('電動車','紅色',4);
console.log(myCar) //Car {type: "電動車", color: "紅色", person: 4}
myCar.carHorn(); //電動車發出車輛的嗚響
但class
的寫法就更加乾淨,而且更易閱讀:
class Car{
constructor(type,color,person){
this.type = type;
this.color = color;
this.person = person;
}
carHorn(){
console.log(`${this.type}發出車輛的嗚響`)
}
}
const myCar = new Car('電動車','紅色',4);
以上Class寫法的結構:
類別{
constructor(prop1,prop2,prop3){
建構函式的屬值
}
放在建構函式的原型物件裏的方法
}
extends
和super
就搞定以前要達成一個下層建構函式繼承上層建構函式的原型物件,我們要:
Object.create
繼承上層建構函式的原型物件.call
方法,呼叫上層的建構函式,才會吃到上層所定義的屬性constructor
會被上層的constructor
蓋掉,但正確做法應該是下層的constructor
指回自己的建構函式,所以我們要把下層的constructor
屬性改回來。function Transportation(transportType){
this.objectType = '交通工具';
this.transportType = transportType;
}
Transportation.prototype.drive = function(){
console.log('我可以駕駛' + this.type)
}
//2. 呼叫上層的建構函式
function Car(type,color,person){
Transportation.call(this,'車輛')
this.type = type;
this.color = color;
this.person = person;
}
//1. Car的原型物件繼承Transportation的原型物件
Car.prototype = Object.create(Transportation.prototype)
Car.prototype.carHorn = function(){
console.log(`${this.type}發出車輛的嗚響` )
}
//3. 把constructor重新指回Car建構函式本身
Car.prototype.constructor = Car
const myCar = new Car('電動車','紅色',4);
但Class語法使整個過程變得更簡短,我們用super
和extends
就能搞定:
class Transportation{
constructor(transportType){
this.objectType = '交通工具';
this.transportType = transportType;
}
drive(){
console.log('我可以駕駛' + this.type)
}
}
//1. Car繼承Transportation
class Car extends Transportation{
constructor(type,color,person){
//2. 呼叫上層建構函式
super(type)
this.type = type;
this.color = color;
this.person = person;
//不能在this後宣告,否則報錯
// super(type)
}
carHorn(){
console.log(`${this.type}發出車輛的嗚響`)
}
}
const myCar = new Car('電動車','紅色',4);
console.log(myCar); //Car {type: "電動車", color: "紅色", person: 4}
myCar.carHorn(); //電動車發出車輛的嗚響
myCar.drive(); //我可以駕駛電動車
在使用super
時有兩點要注意:
extends
後要連帶使用super()
。這樣才可以在new
出一個Car
時所傳入的参數,送到Transportation
這一層,否則Transportation
不知道哪個参數才要被指定成transportType
資料。super()
要在使用this
之前。那麼第3步,Car
的constructor
呢?我們用console.dir(Car)
去查看:
發現Car
的constructor
並不會因為Car
繼承了Transportation
而被蓋掉,它仍然是指向Car
本身!
以下再驗證比對一下:
console.log(Car.prototype.constructor === Car) //true
但是,其實我們有些情況下是不一定要用super()
方法。
如果Car
只是用來新增方法,裏面並沒有屬性,這個情況可以不寫super()
方法,因為new Car()
括號中的参數,一定是傳送到Transportation
裏:
class Transportation{
constructor(type){
this.type = type
}
drive(){
console.log('我可以駕駛' + this.type)
}
}
class Car extends Transportation{
carHorn(){
console.log(`${this.type}發出車輛的嗚響`)
}
}
const myCar = new Car('電動車');
console.log(myCar) //Car {type: "電動車"}
myCar.carHorn(); //電動車發出車輛的嗚響
透過加上static
在方法前面,我們可以把方法變成靜態方法,讓我們可以直接呼叫這個方法。但是,由這個類別產生出來的實體物件則不能呼叫靜態方法:
class Car{
constructor(type){
this.type = type
}
static carHorn(type){
return `我可以駕駛${type}`
}
}
console.log(Car.carHorn('電動車')) //我可以駕駛電動車
Class的語法糖使我們能夠更快捷建立原型鏈,而且讓程式碼更易閱讀。要注意一點是,其實Class和建構函式是在做同一件事,結果都是一樣的。
JavaScript | ES6 中最容易誤會的語法糖 Class - 基本用法
JS 原力覺醒 Day23 - Class 語法糖
你懂 JavaScript 嗎?#21 ES6 Class
原型基礎物件導向