物件導向與 JavaScript

Chen Li-Chi (陳立其)
6 min readOct 3, 2018

--

本章節為 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); // -> 1
var Another = {
cool: function() {
Something.cool.call(this);
}
}
Another.cool();
console.log(Another.greeting);
console.log(Another.count);

其實我不知道 Implicit 的點在哪。這個看起來是利用某 method 去建立其他的 property。並把 parent 的 method 拿過來用。

“nine assorted-color plastic dinosaur toys” by rawpixel on Unsplash

我其實看不太懂

我承認我沒有很看懂這篇想表達的含義。

我得到的重點是,作者強調 class 是一種 copy ,並強調 JavaScript 並不支援 class ,所以這種 copy 在實作上並不好維護。

Classes mean copies.

--

--

Chen Li-Chi (陳立其)
Chen Li-Chi (陳立其)

Written by Chen Li-Chi (陳立其)

希望所有困難事都可以拆成簡單的事

No responses yet