為什麼Javascript需要型別?

banq發表於2014-11-23
VICTOR SAVKIN認為像Javascript和Ruby這樣的動態指令碼語言最好有型別,他在WHY JAVASCRIPT NEEDS TYPES一文中認為:

型別曾經有壞的名聲,因為其使得程式碼能以閱讀,增加不必要的儀式與複雜性,但是在這個博文中我想說明,一個做得正好的型別系統能夠使得程式碼更加可閱讀和工具化,而且非但沒有影響語言的表達能力。

型別其實是對人和機器的文件
讓我們看看型別註釋是如何既對人或機器(計算機)都是有用的文件,看看下面函式jQuery.ajax(),假如我們並不熟悉它,那麼我們從這個方法簽名中能得到什麼資訊呢?
jQuery.ajax(url, settings)

我們唯一能肯定的是,該函式接受兩個引數。 我們可以猜出其型別,也許第一個是一個字串,第二個是一個配置物件。 但這只是一個猜想,我們可能是錯的。 我們不知道進入設定物件有哪些選項(因為既沒有他們的名字也沒有他們的型別)或這個函式返回什麼我們也不知道。

如果我們沒有檢查這個函式的原始碼和文件,我們是沒有辦法呼叫這個函式的。 但是檢查原始碼並不是一個好的選擇,使用函式和類的目的就是能夠不知道他們是如何實現的前提下使用它們。 換句話說,我們應該依靠它們的介面,而不是實現。 因此,原始碼不能檢查了,那麼我們這裡唯一的選擇是檢查文件。 這樣做並沒有什麼錯。 但它需要時間。

對比型別版本:

ajax(url: string, settings?: JQueryAjaxSettings): JQueryXHR;

interface JQueryAjaxSettings {
  async?: boolean;
  cache?: boolean;
  contentType?: any;
  headers?: { [key: string]: any; };
  //...
}

interface JQueryXHR {
  responseJSON?: any;
  //...
}
<p class="indent">

它給予我們更多資訊:

1.這個函式的第一個引數是一個字串。
2.設定引數是可選的。 我們可以看到能傳遞到函式所有的選項,不僅有他們的名字,也有他們的型別。
3.函式返回一個JQueryXHR物件,我們可以看到它的屬性和功能。

這就回到之前觀點:
型別能夠作為程式設計師的文件。

此外,這些資訊能啟用先進的自動完成功能,導航功能和重構能力。 這樣的工具幾乎是大型的專案必備要求。 沒有他們程式設計師會害怕改變程式碼,這使得大規模重構變得幾乎不可能。 理論上來說,您可以使用動態語言構建一個大型應用程式,但是你不能維護它。 因為沒有先進工具的大型程式碼庫總是semi-read-only只讀(banq注:供技術鑑客欣賞賞玩)狀態。型別能夠啟用先進的工具。

型別為程式設計師提供了一個概念性的框架
定義良好的介面意味著是好的設計。 因為它很容易表達想法。

例如,想象一個購書的應用程式,購買可以是兩種角色來完成,一種是透過UI註冊的使用者,還有一種角色是外部系統透過一個API訪問。

[img index=1]

如您所見,類class扮演了一個購買者的角色。儘管角色對於這個應用程式非常重要,但是購買者這個角色概念在程式碼中沒有明確表達了。也沒有一個檔名為 purchaser.js 。 結果,有人修改程式碼後,很有可能錯過角色這個曾經存在過的事實。修改後程式碼如下:

function processPurchase(purchaser, details){
}

class User {
}

class ExternalSystem {
}
<p class="indent">

只是透過檢視程式碼很難知道哪個物件扮演了一個購買者的角色,以及這個角色有什麼方法。 我們確定我們不會從工具中能得到太多的幫助。 我們必須手工人工推斷出這些資訊,這是緩慢且容易出錯

現在我們和一個明確顯式定義了Purchaser介面的程式碼比較:

interface Purchaser {
  id: int;
  bankAccount: Account;
}

class User implements Purchaser {}
class ExternalSystem implements Purchaser {}
<p class="indent">

這個型別版本清楚表明,我們有Purchaser介面,和User以及ExternalSystem類實現這個介面。 這就引出了我的下一個點。

型別對於定義介面/協議/角色等是有用的,它們為程式設計者提供一個概念性框架。

重要的是必須意識到,不強迫我們引入額外的抽象型別。 Purchaser抽象其實已經在這個JavaScript程式碼中存在,只是是沒有被顯式化,也就是被明確定義而已。

我花了大部分職業生涯使用Ruby和JavaScript程式設計,我發現這些語言非常強大和靈活。 但用這些語言編寫相當大的應用程式後,我得出結論,介面的缺乏會推動開發人員建立緊耦合的系統。

在靜態型別語言中使用介面定義子系統之間的界限。因為Ruby和JavaScript缺乏介面,界限就不太能用程式碼表示。 因此不能清楚地看到邊界,開發人員開始只能根據具體型別而不是抽象介面。 它會導致緊耦合。在這些語言中構建大型解耦系統是可能的,但它需要大量的紀律。


似乎我是提倡靜態型別的語言,但我不是。反對強制性的靜態型別系統理由仍然很充分:

1.它使語言複雜化。 在JavaScript編寫一個模擬mock庫是一種幾乎所有人都能實現的練習,但是在Java中做類似的東西是很困難的。
2.它限制了語言的表現力。
3.它需要型別註解,即使他們並不可取,比如使用領域特定語言DSL。

我們想要的是結合動態型別語言的強大和顯式介面和型別註解的好處。

可選的型別系統

一個可選的型別系統不必須使用型別註解。型別註解是用來幫助你表達你的意圖和獲得更好的工具。

可選的型別系統是寬容的。你不必滿足型別檢查器。恰恰相反,型別檢查器可以幫助你找到錯誤,搜尋和重構。 你不需要完全重寫程式以使型別正常執行。 如果你想使用原型和duck typing,就不要使用型別了。 只有當他們能增加價值時使用它們。

也並不是說,可選的型別只是好東西沒有任何缺點。 它還是給語言增加了一些複雜性(相比一個強制性的靜態型別系統)。 同時,充分看到這樣一個系統的好處你就需要比純文字編輯器更復雜的工具。我認為這是值得的。

型別化的Javascript
我在寫這篇部落格之前一段時間,也曾經呼籲“我希望Ruby有介面”。 那時我是一個Rails開發人員。因為我非常沮喪地工作在一個非常大的Rails程式碼庫,一些重構需要花數月才能完成。 因為沒有看到Ruby會有一個可選的型別系統的希望,所以這是一個嘴上的咆哮而已。

但是這一次是不同的。JavaScript社群在擁抱可選型別的想法。 TypeScript 已成為一個在許多公司用於生產的健壯工具。 AtScript , Flow也是這一領域的新專案。 我真的很興奮地看好型別JavaScript社群在以後幾年的未來。



[該貼被banq於2014-11-23 19:09修改過]

相關文章