在介紹完prototype
之後,該是來理解JavaScript
的class
,之前有提到說,JavaScript
的繼承依靠的是Prototypal inheritance
,但並不是所有的程式語言都是如此,我覺得要理解class
的話要從這裡下手,那就開始吧。
之前有提到過繼承,其實道理差不多,一個物件可以將自己的屬性跟方法給其他人使用,換句話說,一個物件可以去取用別人物件的屬性跟方法。
在JavaScript
中,這種可以被取用屬性跟方法的物件是原型物件,透過原型鏈來找到跟綁定,但是在其他語言中,像是java
或是c++
就會直接把這個可以被取用屬性跟方法的物件視為class
,一個類別的概念。
關於類別的比喻,有一個我覺得很不錯,出處是在:JavaScript 概念三明治。
class
其實就是像建築物的設計圖一樣,而建築物就可以透過裡面所描述的概念來寫現做出來。
class
(類別) => 設計圖。object
(物件) => 建築物。
然後有了class(類別)就可以創造出許多的instance
(實例),透過用new
的方式,非常合理,但是JavaScript
做不到。
原因如下:
JavaScript
完全沒有設計 class
的概念。
class
非彼 class
書上看到有這樣寫,不過沒寫原因不太懂,為了探究真相,我開始上網google,下了一些像是what javascript no class?
或是What are classes in Javascript?
的關鍵字。
其中有一篇在stackoverflow上的Does JavaScript have classes?看到很多高手在談論這個議題,最後的結論是確實在JavaScript
是沒有實作class
的機制的。
對,然後你會發現今天的主題也是class
,但這其實沒有衝突,因為都沒有錯,確實是沒有class
但因為想要模擬其他程式語言可以使用class
的特性,所以在es6之後,JavaScript
特別添加了叫做class
的關鍵字。
所以難道這就代表著JavaScript
就此獲得了class
?
我的答案是否定的,沒有的東西就是沒有,JavaScript
從來就沒辦法靠著類別的方式來繼承東西,是依靠原型的,所以就算es6之後有了class的關鍵字,那也是用原型的實作,來看起來很像類別的實作而已,還是不一樣的。
而別的程式語言都用類別方式繼承好好的,為什麼JavaScript
要特立獨行,使用跟別人不一樣的方式(原型),我的猜測是作者最初開始在設計JavaScript
不想要把它設計的太難,這也是它為什麼看起來這麼不嚴謹的原因,這點從JavaScript僅用了10天就創造出來就可以看出。
雖然因為不想要弄的太複雜所以沒有引入class的機制,但還是需要有東西去把所有的物件連接再一起去運作,為此依舊得需要「繼承」的概念。
而上面有提到說,其他很多的程式語言,都是透過new的方式把一個class的東西給創造出來變成instance。
但是JavaScript
沒有class,但是它想到了一個辦法。
那就是使用我們昨天才講到的Constructor Function(構造函式),利用constructor當作像是class一樣,然後藉由去new它一樣可以創造出instance。
可是不會完全跟class繼承一樣,會非常的不完全,因為利用constructor所創造的instance根本沒有辦法共享所有的properies跟methods。
所以才會後來才會創造出,原型這個概念,藉由讓constructor獲得原型的這個屬性,真正的讓constructor所創造的每一個instance都可以共享這個原型物件裡面所有的properies跟methods。
剛剛在那篇高手討論JavaScript
到底有沒有class的文章中,最佳回答是這樣回答的,我覺得蠻合理的:
echnically, the statement "JavaScript has no classes" is correct.
Although JavaScript is object-oriented language, it isn't a class-based language—it's a prototype-based language. There are differences between these two approaches, but since it is possible to use JavaScript like a class-based language, many people (including myself) often simply refer to the constructor functions as "classes".
大意是說,雖然JavaScript
是一種oop的程式語言,但是不是基於class
的語言,而是基於prototype
的語言,雖然這兩種的方式有著差異的存在,但是其實可以把JavaScript
也當作是基於class
的語言(實際上不是),因此許多人也會把constructor
簡單的看做是class
。
小結:JavaScript
沒有class
但是有object
,大家所說在JavaScript
的「class
」實際上是object
(constructor
)。
好,那開始進入這篇文章的主題,Class(類別) => es6新增關鍵字。
第一件事情,JavaScript
中的Class
是一個語法糖,然後語法糖其實我第一次聽到也不知道,所以我有去研究,先來解釋這個部分。
根據維基百科上面的解釋,語法糖是:
語法糖(英語:Syntactic sugar)是由英國電腦科學家彼得·蘭丁發明的一個術語,指電腦語言中添加的某種語法,這種語法對語言的功能沒有影響,但是更方便程式設計師使用。語法糖讓程式更加簡潔,有更高的可讀性。
出處: https://zh.m.wikipedia.org/zh-tw/%E8%AF%AD%E6%B3%95%E7%B3%96
好的,其實就是一個可以用更簡單的方式來表達語法的做法,所以話拉回來,Class這個語法糖確實也可以做到原型繼承的部分,但是它的原理依舊是使用原型的方式,然後其實也會使用到Constructor的概念,很高興這些前幾天都講過了,還沒看的可以回去前幾天複習再往下看,那馬上開始今天的介紹!
直接來做例子來了解其原理。
由於ES6
之後才有Class
,所以之前沒有Class
時,會比較常使用Constructor
來實現原型繼承,ES6
之後有了Class
會用Class
,畢竟是語法糖,方便直覺很多,接下來就來兩種都做做看。
由Constructor
先攻:
function Food(fruit, color) {
this.fruit = fruit;
this.color = color;
}
Food.prototype.saySell = function () {
console.log("快來買蘋果!!!");
};
const apple = new Food("apple", "red");
apple.saySell();
//快來買蘋果!!!
我首先寫了一個準備要被new
建立的函式,第一個英文字母記得大寫(細節),然後透過原型的方式,把方法指定給Food
這個原型裡面,接著用new
來建立構造函式,讓我的apple
這個新的變數,也可以使用saySell
這個方法 => 快來買蘋果!!!
換Class
的回合:
class Food {
constructor(fruit, color) {
this.fruit = fruit;
this.color = color;
}
saySell() {
console.log("快來買蘋果!!!");
}
}
const apple = new Food("apple", "red");
apple.saySell();
這是一個使用了ES6
Class
的神奇方法,跟上面差不多,不過仔細上可以發現許多優點。
先來說明使用Class
的流程,一開始使用Class
這個關鍵字來創,然後Class
後面是名稱,一樣要細節大寫,不同的是,內部就會直接塞一個constructor
的函式,而這個函式的方法,就直接寫在了裡面。
需要方法的話,就直接把方法給塞進去Class
裡面就好了,就在constructor
的函式下方。
這是一件十分酷的事情,因為我就不需要自己再寫什麼.prototype
把方法塞進去,Class
就會自動幫我完成這件事情,語法糖,讚。
因為實驗精神的關係,來證明吧:
console.log(apple.__proto__ === Food.prototype);
// true
結果是ture,確實有原型繼承到。
這一切都跟單純使用Constructor
的方法差不多,但是使用Class
會簡單一些,直覺一些,乾淨一些,結論就是多多使用Class
。
今天的部分來做一個小總結,由上面可以知道,其實Class
的語法有一個固定模式,把它列出來的話會長這個樣子:
class MyClass {
//就是你想的那個構造函式
constructor() { ... }
//這邊直接寫class的方法
method1() { ... }
method2() { ... }
method3() { ... }
...
//剛new就會自動調用constructor()
const vic = new MyClass( ... );
//方法要用自己new後再調用
vic.method1();
}
class
後面的名字不用(),它是用來創造instance
不是用來執行的。constructor
差不多概念。instance
。[1] 原型基礎物件導向
[2] 維基百科 - 物件導向程式設計
[3] What is the difference between classical and prototypal inheritance?