產出好維護好管理的程式碼是重要的,想像今天你的任務是要從這個房間裡找出某一本書,你覺得你需要多少時間?
如果專案程式碼長這樣的話,還沒開始 debug 腦袋就已經先當機了… 而物件導向程式設計就是為了解決這個問題而誕生的一種方便管理程式碼的方式
什麼是物件導向程式設計(Object-Oriented Programming)
- 基於物件概念的程式設計方法
- 使用物件形容(model)具體或抽象的 feature(e.g. user, todo-item, 元件或資料結構)
- 我們把相關屬性跟方法都包在一個物件,可以很方便取得內部資料
- 物件是獨立的(self-contained)的程式碼片段,透過彼此互動構築成一個應用程式
- 公開的 API 即是透過物件內方法跟應用程式互動
物件導向程式語言四大原則
抽象化
- 隱藏實作細節的機制
封裝
- 把屬性跟方法封裝在 class 不讓外部讀取修改,但 class 內部可以讀取
- 封裝好(讓屬性跟方法保持 private)可避免產生非預期 bug
- 當外在程式碼能夠更動到 class 內部資料,會連動影響到調用這個 class 的相關程式碼
- 需要公開存取 API 可設計成開放存取
繼承
- 當有兩個相似的 class,我們可以使用繼承特性避免撰寫重複程式碼(parent class → child class)
多型
- child class 可以覆寫 parent class 的方法
class 跟 instance 實例是什麼?
- class 是一個藍圖(非物件),透過 class 建構出來的東西是 instance 實例
- 但 JavaScript 其實沒有真正的 class 這個類別實例(而 Java,C++有)
- JavaScript 的 class 比較像是語法糖,透過建構函式來模擬 class,可以調用原型(prototype-based)方法
prototype 原型是什麼?
- 在 JavaScript 物件都會連結到原型,原型繼承(prototypal inheritance)表示所有被連結到的實例都可以使用原型的方法
- 舉例來說,我們在使用陣列的 map 方法,也是從陣列的 prototype 取用這個方法,而不是 num 本身有的方法()
建立原型的 3 種方法
- 建立原型是為了可以使用原型繼承
- 使用建構函式(Constructor functions)跟 new 運算子產生新的實例
- ES6 的 class 語法糖
- Object.create(最簡單創造出一個物件連結 prototype 的方法)
|
|
使用建構函式跟 new 運算子建立原型連結
- 寫一個建構函式,使用 new 運算子產生新的物件實例{}
- 當函式被呼叫,物件實例的 this 會指向這個實例本身{} (因為使用 new 運算子才有這個特性,跟一般函式 this 不同)
- 實例會連結到建構函式,讓實例可以使用建構函式的方法,因此每個物件下會有
.__proto__
屬性 - 建構函式本身會 return{}
|
|
檢查實例所屬原型方法
|
|
檢查實例是否含有某屬性 hasOwnProperty
|
|
prototype chain 原型鏈
建構函式 v.s. 原型 v.s. 實例 關係圖
|
|
ES6 的 class 建立原型連結
跟 function 一樣有 expression 和 declaration 兩種寫法
|
|
關於 class 的幾個重點
- class 不會被提升 hoisting(即便寫 class declaration)
- class 具備一級函式特性(可作為參數傳入,也可被回傳)
- class 須在嚴格模式下執行
- class 寫法比第一種(建構函式/const 實例 = new 函式/Person.prototype.方法)更好閱讀與維護,constructor/屬性/方法都寫在 class 裡,方法不需要寫在外面也可以使用 prototype inheritance
getters & setter
- 所有物件都有的屬性(assessor properties),相對於一般屬性稱為 data properties
- 是獲取值或設定值的 function
- 適合用在 validation
|
|
- 可以使用剛剛的 Person 來寫個 get 跟 set function
|
|
static methods
- 使用關鍵字 static 來定義一個靜態的方法(method)給類別(class),靜態方法在由類別所建立的物件實體(instance)上不能被呼叫,靜態方法只能由類別本身呼叫
- 例如 Array.from() 可以把類陣列轉成陣列,但陣列本身不能呼叫這個方法 因為這個方法是放在 Array constructor 身上,而非陣列的原型,所以陣列無法獲取
|
|
- 另一個小練習,get set 時使用另一個變數名(speedUS 而非 speed)就不用擔心撞變數名的問題
|
|
Object.create() 建立原型連結
- 不需要寫建構函式 constructor new,或是.prototype
- 下方範例一般不會這樣做,顯示機制用
|
|
class 間的繼承: 父類別 parent class & 子類別 child class
- 了解如何用 Object.create()在 class 間設置原型鏈,讓父類別原型方法跟屬性可以被子類別繼承
- 子類別繼承父類別的方法跟屬性,也可以有自己的方法跟屬性
- 當子層父層有一樣名稱的方法,會用子層的
使用建構函式跟 new 運算子建立父子類別繼承
|
|
- 子類別實例跟子類別跟父類別關係
|
|
使用 ES6 class 建立父子類別繼承
- 使用 extend 父類別 搭配 super 父類別參數
- super 是父層的建構函式 constructor
|
|
使用 Object.create 建立父子類別繼承
|
|
Encapsulation
- 下底線是讓協作者了解這個是私有的一種表示方式
|
|
- 可以在方法後面 return this 讓方法可以 chain 起來
|
|
- 總結整理
prototype 改寫成 class 練習
- charge 改私有變數,讓外部沒辦法存取
- chain `accelerate’ and ‘chargeBattery’ methods of this class
- update the ‘brake’ method in the ‘CarCl’
|
|
以上為The Complete JavaScript Course - From Zero to Expert的小筆記,附上連結推推這堂課