Rick's DevNotes
筆記關於我作品集
筆記類別
  • 全部
  • DockerDocker
  • NetworkNetwork
  • RxJSRxJS
  • NginxNginx
  • TypeScriptTypeScript
  • Data_Structure_And_AlgorithmData Structure And Algorithm
  • JavaScriptJavaScript
  • PostgreSQLPostgreSQL
  • ReactReact
  • GitGit

© 2026 Rick's DevNotes. All rights reserved.

# Object Oriented Programming# Class

更新時間:2025/08/04

建立時間:2020/09/18

JavaScript 筆記:Prototype、Constructor 與 Class

在學習 JavaScript 的物件導向 (OOP) 時,Prototype、Constructor 與 Class 是核心觀念。以下整理這些概念的關係與使用方式。

原型 (Prototype)

物件導向與 JavaScript

在提到 Prototype 之前,先提一下物件導向程式設計 (OOP)的概念,OOP 是一種用「物件」來組織程式的方式。我們可以把物件想成現實生活中的東西,例如一個人(有姓名、生日、學歷)或一台車(有廠牌、馬力、排氣量)。物件不只可以存放這些資料(屬性),還能包含一些動作(方法),像是計算年齡、讓車子加速或煞車。OOP 的目的是讓程式碼更有條理、彈性高、好維護。雖然 JavaScript 不是傳統的 OOP 語言,但它也有很多類似的概念和做法。

關於建構子 (Constructor)

在物件導向程式設計(OOP)裡,Class(類別)就像是設計圖,可以用來生出很多物件。在 JavaScript 中,這個「生出物件」的動作是靠 Constructor(建構子)來完成的。

以陣列(array)為例,平常我們會寫 const arr = [...] 來建立一個陣列。其實,這個陣列背後是用 Array 建構子創造出來的。我們可以用 .__proto__.constructor 來查詢一個物件的建構子,像下面的程式碼就可以看到 arr1 和 arr2 的建構子都是 Array。

constructor

原型 (Prototype) 與繼承 (Inheritance)

承接上面提到的 arr1 以及 arr2 陣列都可以透過 push 在陣列內新增元素,然而, push 指令並非來自己本身,透過 .hasOwnProperty 即可證明 arr 陣列本身並沒有名為 push 的屬性。

其實,arr 之所以能用 push,是因為 JavaScript 有「原型繼承」這個機制。push 這個方法不是寫在 arr 本身,而是放在它的原型(Array prototype)上。我們可以把這想成 arr 找不到 push 時,會去問它的原型要不要幫忙執行這個方法。

這種繼承的好處是,不管我們建立多少個陣列(像 arr1、arr2 等),每個陣列都不用自己存一份 push、find、filter 這些方法,而是大家共用原型上的方法,節省記憶體。要看建構子的原型,可以用 .prototype,要看物件自己的原型,則用 .__proto__。

with prototype idea
without prototype idea

原型練 (Prototype Chain) 以及 Lookup 機制

在上一段我們用 .hasOwnProperty 來檢查物件有沒有 push 這個屬性。其實,hasOwnProperty 這個方法不是陣列 arr 自己的,也不是它的原型(Array prototype)提供的,而是來自更上一層的原型的原型(Object prototype)。

我們可以把物件想像成一條「原型鍊」(Prototype Chain),每個物件都會連到它的原型,原型又會再連到更上層的原型,一直往上找。當我們執行 arr.hasOwnProperty 時,JavaScript 會先在 arr 本身找這個方法,找不到就去它的原型(Array prototype)找,再找不到就繼續往上到 Object prototype。只要找到就會執行,找不到就一直往上,直到遇到 null 為止。

prototype chain

Constructor 與物件建立

在 OOP 中,Class 像是一張藍圖,用來實體化出不同的物件。在 JavaScript 中,實體化的方式主要有三種:

  1. 函式建構式 (Function Constructor)
  2. ES6 Class
  3. Object.create()

1. 函式建構式 (Function Constructor)

函式建構式的命名慣例為首字母大寫,並搭配 new 來生成物件。

為了避免每個物件重複擁有相同的 methods,通常將方法掛在原型 (prototype) 上:


物件、建構子與 Prototype 的關係

當我們定義方法(methods)時,通常不會直接寫在 Person 裡面。因為如果用 Person 建立 10 個物件,每個物件都會有一份一模一樣的方法,這樣很浪費記憶體。為了避免重複,我們會把方法寫在原型(prototype)上。

另外,Person 的原型和 rick 的原型其實指向同一個物件。只要用 Person.prototype.methodName = function(){...} 來加方法,所有用 Person 建立的物件都能用這個方法。當我們呼叫物件的方法時,JavaScript 會沿著原型鍊(Prototype Chain)去找這個方法。

relationship

建構子之間的繼承

如果想讓物件有更多分類,比如在 Person 建構子裡已經有 firstName 和 birthYear,但又想加上像 country 這樣的新屬性,最簡單的做法是直接加上 rick.country = ...。但如果有很多物件(像 mike、stella),每個都要重複加一次,會很麻煩。這時候可以用建構子的繼承,讓新增的屬性自動加到每個新物件上,省去重複寫程式碼。

如下方程式碼,創建一個新的建構子 ( Taiwanese ) ,透過 Object.create() 將 Peron 的 prototype 傳入 Taiwanese 的 prototype 內。

繼承後的Prototype Chain


2. ES6 Class

ES6 的 Class 其實是函式建構式的另一中表現方式,因為 Class 可以讓程式碼更有更有組織、容易判讀,所以又被稱為函式建構式的語法糖 (syntactic sugar) ,可以將 properties 以及 methods 都寫在一起即可,因此最常被拿來使用。 需要注意的是, Class 不會被提升 (hoist) ,所以在使用前必須先行宣告。

Class 繼承

使用 extends 與 super 來實現繼承:

Class 中的 static

倘若不想要物件繼承原型內的 methods ,可以在函式宣告前面加上 static

3. Object.create() 的應用

物件實字也能作為原型,並透過 Object.create() 建立繼承:

參考資料

  • Jonas's JavaScript Course