ECMAScript 2015 雖然支援了 class 語法,但本質上仍是使用 Prototype 實作,本文深入探討其背後黑魔法。
Version
ECMAScript 2015
Constructor Function
let Book = function(title, price) {
this.title = title
this.price = price
}
Book.prototype.showDescription = function() {
return `${this.title} / ${this.price}`
}
new Book('FP in JavaScript', 100)
ES5 雖然支援 new,卻沒有支援 class,而是搭配 constructor function。
第 1 行
let Book = function(title, price) {
this.title = title
this.price = price
}
Book() 傳入 title 與 price,只負責建立 object 的 property。
其中 this 為稍後 new 所建立的 Object,因此可以直接使用 this.title 新增 property。
Constructor function 習慣使用 PascalCase,有別於一般 function 使用 camelCase
第 6 行
Book.prototype.showDescription = function() {
return `${this.title} / ${this.price}`
}
Constructor function 本質還是 function,而 function 自帶 prototype property 為 {},因此直接對 {} 動態加上 showDescription() method。
prototype 為 function 專屬 property,就真的只是 property 而已,並不代表 function 的 Prototype 是 {},function 的 prototype 是由 __proto__ 指向 Function.prototype。
此外,Book.prototype.constructor 會指向 Book(),所以將來 book.constructor 也會指向 Book(),稍後 book.__proto__ 會使用 book.constructor 找到其 Prototype Object。
第 10 行
let book = new Book('FP in JavaScript', 100)
當 function 搭配 new 時,就搖身一變成為 constructor function,this 為所建立的 Object。

可發現 showDescription 不屬於 book Object,而是在其 Prototype Object,因此在 __proto__ 下。
我在學習 Prototype 時,最大卡關是觀念上明明應該對 Object 的 Prototype Objecy 新增 method,為什麼到最後是對 constructor function 的
prototypeproperty 新增 method 呢 ?
當執行 showDescription() 時,會發現 book Object 沒有 showDescription(),此時會由 __proto__ 往其 Prototype Object 尋找 showDescription().。
由於 Book.prototype.constructor() 指向 Book(),所以 book.constructor() 亦指向 Book(),__proto__ 可藉由 book.constructor property 找到 constructor function,再又其 prototype property 找到 Prototype Object,如此就可藉由 Prototype Chain 找到 showDescription()。
可見 Book.prototype.constructor = Book 是 __proto__ 能找到 Prototype Object 的線索。
由上圖可發現 Book() 的 prototype property 與 book Object 的 __proto__ property 都指向相同的 Book.prototype,也因此我們可藉由動態新增 Book.prototype 的 method 成為 book 的 __proto__ 的 method,其關鍵就是 book Object 的 constructor property 指向 constructor function,而 constructor function 的 prototype property 指向 Book.prototype。
Class
class Book {
constructor(title, price) {
this.title = title
this.price = price
}
showDescription() {
return `${this.title} / ${this.price}`
}
}
new Book('FP in JavaScript', 100)
ES6 支援 class 後,語法又更簡單了,連 prototype 字眼完全沒看見。

但可發現 showDescription 不屬於 book Object,而是在其 Prototype Object,因此在 __proto__ 下。
這也再次證明 class 只是 syntatic sugar,ECMAScript 依然是 Prototype-based,而非 Class-based。
Conclusion
__proto__只所以能找到其 Prototype Object,關鍵在constructorproperty 指向 constructor function,再由 constructor function 的prototypeproperty 找到 Prototype Object,這就是 Prototype Chain 的黑魔法- 隨著時代的進步, ECMAScript 寫法不斷精簡,只是黑魔法越來越大,但其 Prototype-based 本質不變
- ES6 的 class 寫法雖然完全看不到 Prototype,但本質仍是將 method 放在 Prototype Object,是道地的 syntatic sugar
Reference
MDN, Object.prototype.constructor
MDN, Object prototypes
MDN, Object.create()