有一句話時常在網路上看到:ECMAScript 2015的出現,讓JS具有完整的class特性了!
但是這句話其實是錯的,你可能會問為什麼?不是從那個時間點開始有了class可以用的嗎?
但是其實class在JavaScript中,說穿了不過就是一個糖衣罷了,要實現class的特性,主要還是基於Prototype上運作。
讓我們看以下的範例

Prototype

讓我們來用Prototype來實現一般我們所熟悉的class來看看吧!

  1. constructor
    這個function我們可以視為是類別的一個constructor
    當function被建立的時候,其實就可以將這個行為視為是一種constructor

    1
    2
    3
    function Pet(name) {
    this.name = name;
    }
  2. 宣告內容在protoType內
    在你所宣告的function內,你可以在它的prototype裡面放入你想放的內容

    1
    2
    3
    Pet.prototype.meow = function meow() {
    console.log(this.name + ' meow~');
    }
  3. 測試
    這樣我們就建構好一個類別了,再來宣告物件使用看看

    1
    2
    let pet = new Pet('Coco')
    pet.meow()
  4. 如果不用new呢?
    JS不會跟你說有錯誤,但是你會發現無法呼叫
    原因是,你所執行的狀況,是改到window了

    1
    2
    3
    pet = 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
2
3
4
5
6
7
8
9
10
11
12
class cat extends Pet {
constructor(name){
super(name);
this.scream = 'xxxxxx';
}
meow() {
super.meow();
}
static letMeow() {
console.log('meow');
}
}

當你把以上程式碼編譯之後,試著呼叫這個物件並且打開裡面的結構,你會發現這個結構根本就是Prototype!
主要原因是,使用extends語法產生Prototypal chain,即可達到效果。

小節

JS因為自由度的問題,時常會造成很多人的誤解和誤用,但是真實地去瞭解理解它,會發現很多有趣的地方和執得討論的部分。