物件導向與 JavaScript
本章節為 YDKJS 的讀後心得,主要在講物件導向用在 JavaScript 上面要留意的事情,因為 JavaScript 並沒有物件導向意義上的 classes。
所以如果我們要完成物件導向這個設計模式在 JavaScript 上面,我們會需要下列東西。
- class
- instantiation
- inheritance
- polymorphism
物件導向與人類世界存在某種聯繫,以交通工具 (Vehicle) 為例,我關心的並不只是交通工具組成材料是什麼,而更關心的事,他有什麼功能。像這種把材料跟功能包裝起來 (encapsulate) 的模式,就是物件導向的概念。
Class Theory
class 與 object 的關係,就像設計藍圖與實際建築,就像 DNA 與生命體。而 class 到產生 object ,我們稱為 instantiation,有時候產生的 object ,我們也會叫他 instance 。
這篇文章通篇都在灌輸一個觀點,那就是 class 給人一種複製的概念。我們把設計藍圖上的東西,複製到建築物中。
舉另一個軟體界聞名的例子,交通工具 (Vehicle) 與車子 (Car) 。雖然這兩個東西同樣都是 class,但是我們可以定出他們的繼承關係 (inheritance) ,Car inherits Vehicle ,讓 Car 可以複製一份 Vehicle 的特性到自己身上。
繼承了之後也不一定要原封不動的繼承,你有聽過青出於藍勝於藍嗎,原本我繼承了師傅的煮麵的手藝,但我可以把它變得更強,變成另一項與原本不同的手藝,但他們同樣都叫做煮麵。
我想到一個經典的比喻是關於英雄聯盟 (League of Legend) 角色的重置,像是史瓦妮,卡瑪都曾經進行重置過,可以把重置後的新史瓦妮,新卡馬視為繼承舊的史瓦妮,卡瑪。一旦繼承了之後,雖然預設經典造型變了 (polymorphism) ,但是並不是複寫掉舊的造型,你仍然可以選元祖造型來用。
但是 JavaScript 要這樣會比較麻煩一點,因為他並沒有內建物件導向的 class 型態,並不是複製一份到 object 或是 child 中。所以一旦 instantiate/inherit 之後很容易不小心覆寫掉原本的 method ,導致找不到 parent method 。
Explicit mixins
function mixin(sourceObj, targetObj) {
for (var key in sourceObj) {
if (!(key in targetObj)) {
targetObj[key] = sourceObj[key];
}
}
return targetObj
}var Vehicle = {
engines: 1,
ignition: function() {
console.log("Turning on my engines.");
},
drive: function() {
this.ignition();
console.log("Steering and moving forward!");
}
}var Car = mixin(Vehicle, {
wheels: 4,
drive: function() {
Vehicle.drive.call(this);
console.log("Rolling on all " + this.wheels + " wheels!");
}
})
在 Car Object 的 drive 裡面,如果我們不直接呼叫 Vehicle.drive.call(this)
的話,還真的不知道怎麼用相對的方法直接呼叫 parent 的方法。這就是 JavaScript 之所以對物件導向不太方便的原因之一。
Parasitic inheritance
function Vehicle() {
this.engine = 1;
}
Vehicle.prototype.ignition = function() {
console.log("Steering and moving forward");
}
Vehicle.prototype.drive = function() {
this.ignition();
console.log("Steering and moving forward!");
}function Car() {
var car = new Vehicle;
car.wheels = 4;
var vehDrive = car.drive;
car.drive = function() {
vehDrive.call(this);
console.log("Rolling on all " + this.wheels + " wheels!");
}
return car;
}var myCar1 = new Car();
myCar1.drive();var myCar2 = Car();
myCar2.drive();
我們可以發現在 Car 裡面偷偷先 new 了一個 Vehecle 出來,這時就有 parent 的 method 可以用,所以先用 vehDrive
存下等等要用到但會被覆寫掉的東西。最後 return 這個 new 出來但是被我們修改過的 Vehicle instance 。
如果在 Constructor 有回傳值的話,就不會回傳 new 原本預設幫你建立好的 Object 。所以這時候有沒有 new 都會產生一樣的結果。
Implicit mixins
var Something = {
cool: function() {
this.greeting = "Hello World";
this.count = this.count ? this.count +1 : 1;
}
}Something.cool();
console.log(Something.greeting); // -> Hello World
console.log(Something.count); // -> 1var Another = {
cool: function() {
Something.cool.call(this);
}
}Another.cool();
console.log(Another.greeting);
console.log(Another.count);
其實我不知道 Implicit 的點在哪。這個看起來是利用某 method 去建立其他的 property。並把 parent 的 method 拿過來用。
我其實看不太懂
我承認我沒有很看懂這篇想表達的含義。
我得到的重點是,作者強調 class 是一種 copy ,並強調 JavaScript 並不支援 class ,所以這種 copy 在實作上並不好維護。
Classes mean copies.