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 的
prototype
property 新增 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,關鍵在constructor
property 指向 constructor function,再由 constructor function 的prototype
property 找到 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()