Skip to content

Class 類

Javascript 本身沒有 class (類),在 ES6 出現新語法 class 就是依 Constructor 建構函式 語法糖包裝的,它的底層與 Constructor 建構函式 是完全一樣,只是提供更簡潔的語法來建立物件與處理繼承。

簡單說

Class 只是簡化了 JavaScript 中操作 Constructor 的語法糖。

建立基本 Class

js
class Person {
  // 定義實例屬性
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  //  定義 prototype 原型方法
  sayHello() {
    console.log(`Hello my name is ${this.name}`)
  }
}

流程

  • 聲明 class 的命名物件 {}
  • 定義 屬性資料
    • 內部創建一個 consturctor 的函式,來定義這個實例的 屬性資料
  • 定義 原型方法 prototype
    • 內部直接寫上 函式 (這個部份,就是 建構函式中的 prototype 的方法) 之後也可以透過 .prototype 來新增函式方法

創建實例

Constructor 建構函式 一樣都是使用 new 關鍵字。

js
const niki = new Person('niki', 6)

注意

class 沒有 hoisting (提升),你必須先宣告才可以使用,不然會出現錯誤。

js
// ReferenceError
const niki = new Person('niki', 6)

class Person{ ... }

原型方法 prototype

可以提供 實例 instance 共享操作方法的設置,直接在 class 物件內建立函式,就是原型方法了。

js
class Person {
  // 定義實例屬性
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  //  定義 prototype 原型方法
  sayHello() {
    console.log(`Hello my name is ${this.name}`)
  }
}

建立子類別 (extends)

建立 class 時,加上 extends 關鍵字,可以用來繼承 父層 class原型方法,同時也會將 父層 屬性資料,變成 子層 屬性資料 (寫入)。

class 子層 extends 父層

js
class Parent {
  constructor(name) {
    this._parentName = name
  }
  sayParentName() {
    console.log(this._parentName)
  }
}

class Child extends Parent {
  sayChildName() {
    console.log('Child')
  }
}

const niki = new Child('NIKI')

說明

子層 會繼承 父層屬性資料父層_parentName 會變成 子層屬性資料,當 子層實例 傳入 參數 時,就會設置成 _parentName子層實例 除了有本身的 原型 也繼承了來自 父層 原型

Error

子層 有設置 constructor 時,就必須要使用 super 不然就會報錯:

Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

呼叫父類別 (super)

在繼承 父層 class 情況下的 子層 class,在可在物件內使用 super 來操作 父層 class

js
// 父層
class Parent {
  // 父層 屬性資料
  constructor(name) {
    this._parentName = name
  }
  // 父層 原型方法
  sayParentName() {
    console.log(this._parentName)
  }
}

// 子層 (繼承父層)
class Child extends Parent {
  // 子層屬性資料
  constructor(parentName, childName) {
    super(parentName) // 父層 屬性資料 賦值 (帶入父層參數)
    this._childName = childName
  }
  // 子層 原型方法
  sayChildName() {
    console.log(this._childName)
  }
  sayParentName() {
    console.log(`child sayParentName fun`)
    super.sayParentName() // 操作父層 原型方法
  }
}
js
const niki = new Child('Naiky', 'NIKI')
niki.sayParentName()

// child sayParentName fun
// Naiky

說明

子層 constructorsuper 帶的參數,會直接送到 父層 constructor,再回頭寫入 子層 constructor

子層 prototype 函式內的 super.sayParentName(),就是直接操作 父層 prototypesayParentName 函式。

簡單說

super 代表 父層 class 物件。

注意

  • 子類super(),在放在 this 使用之前。
  • 子層 constructor 的屬性名稱與 父層 constructor 屬性名稱 相同 時,會以 子層 為主 (覆蓋)。
  • 子層 prototype父層 prototype 同名 時,會先執行本身 子層 prototype。(若無,向上層尋找)

私有屬性

當屬性名稱以 # 為前綴時,就會被認為是「私有」的屬性,只可以在聲名這個變數的 class 中被訪問,外部無法訪問。

js
class Person {
  #firstName
  constructor(firstName) {
    this.#firstName = firstName
  }
  get firstName() {
    return this.#firstName
  }
}

const niki = new Person('NIKI')

niki.firstName // 'NIKI'
niki.#firstName // Uncaught SyntaxError: Private field '#firstName' must be declared in an enclosing class

Reference