類別(class)是什麼?|JavaScript 初學者筆記(4)

更新日期: 2024 年 4 月 1 日

身為一位 Javascript 菜雞,小弟在學習「類別」(class)時總是感覺異常疑惑。

後來才發現若想要對「類別」有更深入的理解,一定要先理解「原型」(prototype)的相關概念,才能更容易知道運作邏輯。

因為類別的本質,就是基於原型衍伸出來的語法糖。

本文,就是要針對「類別」此概念,向如同自己一樣的程式新手,盡可能白話地解釋相關意涵。

什麼是類別(Class)?

如果我們能理解上一篇文章中,物件「原型」(prototype)、「原型繼承」(prototype-inheritance)等相關概念,對於「類別」(Class)就能更簡單的認識。

原因在於,「類別」就是基於「原型繼承」相關模型衍生出來的「語法糖」(Syntactic sugar)

它最初是在 JavaScript 2015 年版本(ES6)出現的語法,主要在於提供使用者以更簡潔的方式,使用物件原型的相關代碼。

以下,我們分別針對「類別建構子」與「類別方法」這兩點進行討論。

類別建構子(Class Constructors)

Class 語法之前,若我們想要建立一個物件,可以使用建構子函式此方法。

例如若我們想要建立使用者物件,可以先建立一個建構子:

function User(name, age, id) {
    this.name = name;
    this.age = age;
    this.id = id;
}

再搭配使用 new 關鍵字,建立一個物件實體。

function User(name, age, id) {
    this.name = name;
    this.age = age;
    this.id = id;
}

const user01 = new User("徐培鈞", 25, 9527);

如果你看不懂這上述代碼,建立你先理解一下建構子的概念,補充背景知識。

在 ES6 版本中,我們可以將上述代碼轉換成另一種形式。

class User {
    constructor(name, age, id){
        this.name = name;
        this.age = age;
        this.id = id;
    }
}

const user01 = new User("徐培鈞", 25, 9527);

我們可以發現,原先的 function 關鍵字被 constructor 取代,在外圍又包裹了一層 class 語法。

你可能想說:「哪有變更簡潔,原先只有一層 function 代碼,現在外面又多包了一層 class 代碼?」

你想懂!?本文下面有些好康的,請繼續閱讀下去。

類別方法(Class Methods)

我們除了能在建構子中建立屬性(property)成員,也能新增一些方法(method)成員。

例如新增一個 showId 函式:

function User(name, age, id) {
    this.name = name;
    this.age = age;
    this.id = id;
    showId = function (){
        console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
    }
}

貼心叮嚀:在建構子中函式的寫法,有兩種不同形式,不要忘記嘍

// 寫法一
function User(name, age, id) {
    showId = function (){
        console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
    }
}

// 寫法二
function User(name, age, id) {
    showId(){
        console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
    }
}

當然,我們前一篇文章也說過,由於我們希望自己建立出來的實體,有更簡潔的代碼呈現方式,因此通常會將方法成員丟進原型(prototype)中,只保留屬性成員而已。

原先代碼會轉變成以下:

// 區塊一
function User(name, age, id) {
    this.name = name;
    this.age = age;
    this.id = id;
}

// 區塊二
User.prototype.showId = function(){
    console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
}

這時你會發現,代碼因為需要使用到原型的屬性,因此被拆解成兩個區塊。

這時,新版的 class 關鍵字就能起到整合的作用,將原先分開的兩個部分重新包再一起,方便使用者閱讀。

class User {
    constructor(name, age, id){
        this.name = name;
        this.age = age;
        this.id = id;
    }

    showId(){
        console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
    }
}

const user01 = new User("徐培鈞", 25, 9527);

我們可以發現 class 關鍵字不只是針對建構子優化,同時還在原型方法的使用納入其中。

這一切的調整,其實就是為了後續的閱讀與使用而已。

什麼是方法鍊(Method Chaining)

對於一位 JS 新手來說,通常學習到函式的使用都是一次呼叫執行一項函式。

function xxx(){
    //..........
}

function ooo(){
    //..........
}

xxx();
ooo();

這種為單一函式各自呼叫一次的做法,雖然操作起來簡單理解,但是當函式數量變多時,就會使代碼變的混亂、難以理解。

你可試想如果上述的例子函式,從 2 個暴增到 20 個以後,又會是一個怎樣的光景!?

「方法鏈」(Method Chaining)這個技術,就是為了解決以上問題而出現的機制。

它將原本散落在各處呼叫函數的代碼,簡化並壓縮到只剩一行代碼。

// 普通方式
物件.xxx();
物件.ooo();
物件.zzz();

//函式鏈
物件.xxx().oooo().zzz();

具體來說,它是透過 this 關鍵字會指向原本物件實體本身的特性,再搭配 return 關鍵字回傳,讓每一次執行完物件方法後,都會不斷回傳該物件實體本身。

如此,就能以一行代碼執行多次物件方法。

套用到實際例子,如果我們將 User 建構子再新增一個方法,取名為 answer

class User {
    constructor(name, age, id){
        this.name = name;
        this.age = age;
        this.id = id;
    }

    showId(){
        console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
    }

    answer(){
        console.log(`回答:代號 ${this.id} 記住了。`);
    } //新增此段代碼
}

const user01 = new User("徐培鈞", 25, 9527);

如果我希望能夠一次就呼叫使用 showId answer 兩個方法,可以再各自的函式中增加 return this 此關鍵字

class User {
    constructor(name, age, id){
        this.name = name;
        this.age = age;
        this.id = id;
    }

    showId(){
        console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
        return this; //新增此段代碼
    }

    answer(){
        console.log(`回答:代號 ${this.id} 記住了。`);
        return this; //新增此段代碼
    } 
}

const user01 = new User("徐培鈞", 25, 9527);

如此一來,就能順利藉由以下代碼,一次呼叫多個方法。

user01.showId().answer();

類別繼承(Class Inheritance)

「類別繼承」(Class Inheritance)是指一個類別「繼承」另一個類別的方法與屬性。

在 ES6 版本前,我們是透過「原型繼承」此方法完成,使用 Apply 方法繼承屬性、使用 Object.create 繼承原型方法。

不知道我在供蝦咪的人,可以點擊連結查看說明。

在 ES6 版本以後,若我們可以藉由 class 搭配 extend 關鍵字,完成相同的目標。

class User {
    constructor(name, age, id){
        this.name = name;
        this.age = age;
        this.id = id;
    }

    showId(){
        console.log(`${this.name},${this.id} 是你的終身代號,記住了嗎!?`);
        return this; //新增此段代碼
    }

    answer(){
        console.log(`回答:代號 ${this.id} 記住了。`);
        return this; //新增此段代碼
    } 
}

const user01 = new User("徐培鈞", 25, 9527);

我們將上方代碼,藉由圖像描繪出 user01 實體與 User 建構子之間的關鍵。

類別繼承(Class Inheritance)關係圖

假如我們想要創立另一個 Admin 建構子,它會繼承 User 建構子所有的方法與屬性,並擁有權限使用他們。

我們就必須使用以下代碼:

class Admin extends User {

}

const Admin01 = new Admin("店長", 33, 8787);

這樣,我們就完成 Admin 的繼承動作了。

如果我們想要針對 Admin 新增一個它獨有的方法:meet,只需要在接續寫在尖括號「{}」中即可。

class Admin extends User {
    meet() {
    console.log(`${this.name} 請支援收銀!`);
  }
}

const Admin01 = new Admin("店長", 33, 8787);

我們將代碼轉換成圖下,就能理解彼此之間的關係。

類別繼承(Class Inheritance)關係圖

以上就是針對類別、類別繼承的說明,我們下篇文章見!

Similar Posts