點燈坊

失くすものさえない今が強くなるチャンスよ

你所不知道的 Class

Sam Xiao's Avatar 2020-01-01

Class 是 OOP 必備觀念,ECMAScript 2015 總算支援 Class,讓很多人倍感親切,但事實上 ECMAScript 的 Class 並非如你想得這麼單純。

Version

macOS Catalina 10.15.2
VS Code 1.41.1
Quokka 1.0.267
ECMAScript 2015

Class

class Book {
  constructor(title, price) {
    this.title = title
    this.price = price
  }
  
  showDescription() {
    return `${this.title} / ${this.price}`
  }
}

let book = new Book('FP in JavaScript', 100)
book.showDescription() // ?

這是典型 ES6 的 class 寫法,由 constructor keyword 定義 constructor,且 method 直接定義在 class 內,而非透過 prototype 新增 method,十足 OOP 風,一切看似完美。

overview000

typeof Operator

class Book {
  constructor(title, price) {
    this.title = title
    this.price = price
  }
  
  showDescription() {
    return `${this.title} / ${this.price}`
  }
}

typeof Book // ?

若使用 typeof 判斷 Book 型別,竟然回傳 function

overview001

這已經告知 class 實際上只是 constructor function 的 syntatic sugar,Book class 在底層仍是 Book() function,只是看起來像 class 而已。

Book.prototype.constructor

class Book {
  constructor(title, price) {
    this.title = title
    this.price = price
  }
  
  showDescription() {
    return `${this.title} / ${this.price}`
  }
}

Book === Book.prototype.constructor // ?

若判斷 Book class 與 Book.prototype.constructor 會回傳 true,也就是根本指向相同 object。

overview002

overview003

也就是 Book class 本質還是 constructor function,因此 Book.prototype.constructor 依然指向 Book(),因此回傳 true

Prototype Chain

class Book {
  constructor(title, price) {
    this.title = title
    this.price = price
  }
  
  showDescription() {
    return `${this.title} / ${this.price}`
  }
}

Book.prototype.getTitle = function() {
  return this.title
}

let book = new Book('FP in JavaScript', 100)
book.getTitle() // ?

12 行

Book.prototype.getTitle = function() {
  return this.title
}

既然已經證明 class 本質是 constructor function,若對 Book.prototype 新增 getTitle(),ES6 也會依照 prototype chain 找到 getTitle() 正常執行。

overview004

Function Call

class Book {
  constructor(title, price) {
    this.title = title
    this.price = price
  }
  
  showDescription() {
    return `${this.title} / ${this.price}`
  }
}

Book.prototype.getTitle = function() {
  return this.title
}

let book = Book('FP in JavaScript', 100)

16 行

let book = Book('FP in JavaScript', 100)

Constructor function 仍可被呼叫,只是回傳 undefined 而已,但 class 則完全無法當成 function 被呼叫,直接 syntax error。

overview005

Conclusion

  • ES6 雖然支援 class keyword,但 ECMAScript 本質仍是以 object 與 function 為主的語言,class 只是 syntatic sugar,讓其他語言背景學習 ECMAScript 時能快速ㄉ上手,但底層仍以 constructor function 與 prototype 實作
  • class 雖然本質是 constructor function,但還是有些不同,如 constructor function 可被直接呼叫,只是回傳 undefined 而已;但 class 則完全無法當 function 使用,直接 syntax error

Reference

許國政, 008 天重新認識 JavaScript