JavaScript | Prototype vs. Class 語法糖有什麼差別?

Leo Chiu
手寫筆記
Published in
7 min readSep 11, 2020
Photo by ZQ Lee on Unsplash

前言

MDN 上是這樣說的:「ECMAScript 6 中引入了類別 (class) 作為 JavaScript 現有 prototype-based 繼承的語法糖。class 語法糖並不是要引入新的物件導向繼承模型到 JavaScript 中,而是提供一個更簡潔的語法來建立物件和處理繼承。」

如果說只是個語法糖,照理來說 class 應該跟使用 function 定義 prototype 差不多,只是語法更簡潔。但是前陣子有個人考我「prototype 跟 class 語法糖差在哪」,而我僅僅知道 class 只是個語法糖,以為沒什麼差別,儘管透過 babel 向下轉換成 ES5 的語法也看不出個所以然。

然而對方很確信 prototype 與 class 語法糖真實存在不一樣的地方,所以就花了一些時間研究兩者實際上的不同,整理了 7 個平常比較有機會接觸到 prototype 與 class 語法糖不一樣的部分。

以下文章中「class 語法糖」會簡稱為「class」,與 class-based inheritance 中的 class 無關。此外,在文章中如果使用 prototype 為主詞,則代表的是使用 function-based 建立物件的方式。

prototype vs. class syntactic sugar

class 一定要使用 new,否則會出現錯誤

在 ES6 之前,要使用 prototype,都必須以 function 為基準建立 prototype 物件,既然都是基於 function,當然可以選擇要使用 new operator 建立物件的 instance,或是把它當作一般的 function 使用。

然而,class 不一樣的地方在於只能使用 new operator 建立物件的 instance。以底下的 class User{} 為例子,雖然 typeof User 的結果為 function ,但是它不能當作一般的 function 使用。

如果硬是要讓 class 當作 function 使用也不是不行,只是做法有點「神奇」,可以讓 class 繼承 Function,如此一來就可以將 new operator 產生的 instance 作為 function 使用 (後面會提到)。

在 class 中定義的 function 不是 constructor

在 class 中定義的 function 其 prototype 是 read-only,這意味者我們不能在 function 的 prototype 上定義新的屬性或函式,同時也不能將它作為一個物件,呼叫 new operator 建立 instance。

在一般 function 上的 prototype 定義的 function 則可以是一個 constructor,這意味者你可以將它當作是一個新的物件使用,使用 new operator 建立 instance。

但是我想應該沒有人會這樣用吧 😅。

class 的 function 是 non-eumerable

我們先看到使用 prototype 定義物件的方式,不論是使用 this 直接定義屬性的方式,或是在 prototype 上新增 function,都可以用 for...in 的迭代物件的屬性。

然而,我們看到 class 的範例,雖然可以使用 for...in 迭代使用 this 定義的屬性,但是卻無法取得在 class 中定義的 function name。

class 沒有 hoisting 的機制

我們都知道 JavaScript 有一種 hoisting 的機制,在執行 function 之前,會將 function 先放到記憶體裡面,所以 function 可以在定義前被呼叫。

但是 class 不一樣,雖然 typeof User 的結果是 function ,然而 class 是不能被 hoisting 的,所以如果在定義前使用 class,則會得到 Reference Error 的錯誤訊息。

class 可以繼承 Function

在 ES6 後出現的 class 語法糖真的是一個神奇的東西,這次為了研究 class 與 prototype 的差別時,才發現 class 有這麼神奇的使用方法。

誰會想要讓 class 繼承 Function!? 😟

這種方法雖然看起來很酷,但是透過繼承 Function 把 class 當作一般 function 使用,在團隊裡面不被打死才怪 😆。

而 prototype 就沒那麼彈性,可能是 JavaScript 一開始的設計就不能讓我們這樣用,畢竟 function 原本就已經繼承 Function.prototype,再繼承一次 Function 好像跟原本沒什麼差別,所以透過 new operator 建立的 instance 也就是普通的物件。

class 不能夠被重複定義

function 可以被以同樣的名稱重複宣告,但是 class 不能夠重複宣告,class 像是 letconst,重複宣告時都會有錯誤訊息。

class 屬於 block scope

classletconst 同樣都是 block scope,以下範例中,我們在 {} 裡面定義一個 class,但是這個 class 只要離開了 {} ,就無法看到。

function 不一樣的地方在於,如果我們在 {} 中定義一個 function,則該 function 會被放到 window 中,所以在 {} 外是可以看到這個 function 的。

[2020/09/13 4:50 更新]

class 允許 computed method name

這個使用方法比較少看到有人會這樣使用,在 class 中使用 computed method name 給予我們多一點彈性定義 function 的名稱,甚至可以使用變數的數值定義 class 中的 function。

而 function 對應的語法則是像在物件上定義一個屬性,使用 obj[key] 的語法,就可以在 prototype 使用 computed method name 彈性的定義 function。

結論

不知道你是不是以前就知道這麼多不同之處? 整理完這篇文章才發現 prototype 與 class 語法糖有許多不一樣的地方 😅。

可能有些是 class 與 function 的比較,不全然是找出與 prototype 的不同之處,但因為 prototype inheritance 在過去基本上都是基於 function 建立的,所以在搜集資料時也一併整理近來。

分享就到這邊,如果喜歡我的文章可以幫我拍個幾下手,在閱讀文章時如果有遇到什麼問題,或是有什麼建議,都歡迎留言告訴我,謝謝。 😃

--

--

Leo Chiu
手寫筆記

每天進步一點點,在終點遇見更好的自己。 Instragram 小帳:@leo.web.dev