JavaScript的Prototype與Class
有一句話時常在網路上看到:ECMAScript 2015的出現,讓JS具有完整的class特性了!
但是這句話其實是錯的,你可能會問為什麼?不是從那個時間點開始有了class可以用的嗎?
但是其實class在JavaScript中,說穿了不過就是一個糖衣罷了,要實現class的特性,主要還是基於Prototype上運作。
讓我們看以下的範例
Prototype
讓我們來用Prototype來實現一般我們所熟悉的class來看看吧!
constructor
這個function我們可以視為是類別的一個constructor
當function被建立的時候,其實就可以將這個行為視為是一種constructor1
2
3function Pet(name) {
this.name = name;
}宣告內容在protoType內
在你所宣告的function內,你可以在它的prototype裡面放入你想放的內容1
2
3Pet.prototype.meow = function meow() {
console.log(this.name + ' meow~');
}測試
這樣我們就建構好一個類別了,再來宣告物件使用看看1
2let pet = new Pet('Coco')
pet.meow()如果不用new呢?
JS不會跟你說有錯誤,但是你會發現無法呼叫
原因是,你所執行的狀況,是改到window了1
2
3pet = Pet('Coco')
pet // undefined 因為預設是var,改到global var
window.name // name跑到window了
如何查找Prototype
當你打開瀏覽器的console的時候,將你宣告的物件往下拉,你會看到剛剛放入的name和proto
而這裡面包含了剛剛放入的function,還有一開始建置的constructor
然後你又會看到內部包含了proto,這就是所謂的Prototypal chain
共享
如果你建置了很多function,當你執行以下的程式碼
1 | delete pet.__proto__.meow |
會讓所有的function失去這個內容,原因是所有的物件是共享這個prototype
當你更動,就會影響到所有的物件
MDN解釋
JavaScript 對那些從 Java 或 C++ 學過來的人來說,可能會有點困惑,因為它動態、永遠是執行狀態(all runtime)、還完全沒有 class。一切都只是實例(物件)。即使是「class」關鍵字,也只是函式物件。
更改Prototype
同上述的共享可得知,如果更改了Prototype,同樣也會影響到內部所共享的function
1 | pet.__proto__ = animal.__proto__; |
測試
可以使用以下程式進行測試,你會發現類型是一樣的
1 | pet instanceof animal === true |
Prototype模擬OO model
以下的模擬可以讓你更容易了解,為什麼ES6的OO所做的事情在以前可以用Prototype做到
我們先用ES6的class syntax模擬OO:
1 | class cat extends Pet { |
當你把以上程式碼編譯之後,試著呼叫這個物件並且打開裡面的結構,你會發現這個結構根本就是Prototype!
主要原因是,使用extends語法產生Prototypal chain,即可達到效果。
小節
JS因為自由度的問題,時常會造成很多人的誤解和誤用,但是真實地去瞭解理解它,會發現很多有趣的地方和執得討論的部分。