TypeScript與Haxe:兩種截然不同的JS轉譯工具橫向對比

jopen 9年前發布 | 12K 次閱讀 TypeScript

 

JavaScript無疑是當今最火爆的編程語言之一,它的崛起要歸功于AJAX、Node.js的出現以及時下各種MVC框架的流行。但作為一門在十天之內創建出來的語言,JS本身存在著一些不完善之處、以及容易令人疑惑的地方,例如不支持強類型以及new關鍵字的用法。尤其與當前主流的面向對象語言以及動態語言相比,其不足之處顯得尤為突出。

為了克服JS語言中的缺陷,讓更多的人能夠編寫出優秀的代碼,市面上出現了 大量 能夠將其它編程語言轉譯為JS的工具。其中較知名的有 CoffeeScriptDartGWTScript# ,以及本文的主角 TypeScriptHaxe

TypeScript是這一領域中的新貴,由微軟于2012年發布。近期發布到1.5版,加入了大量ES6的特性。TypeScript被設計為 JS的一個超集,因此現有的JS都是合法的TypeScript代碼。這門語言也得到了Angular團隊以及Telerik的支持。

作為這一領域中的老前輩,Haxe從2006年起就提供了轉譯為JS的功能。與TypeScript類似,Haxe也提供了一套類似于JS的語法、靜態類型系統以及模塊。除了JS之外,Haxe還能夠提供編譯為Flash、PHP、C++等語言的功能。

來自Haxe的員工Andy Li最近在 博客 中對TypeScript與Haxe進行了一次全面的比較,包括語法、底層語義、類型系統、以及組織和生成代碼的方式。為開發者如何在這兩種工具間進行選擇提供了一個不錯的參考。

語法

TypeScript與Haxe的基礎語法是非常相似的,它們都支持在定義變量時聲明類型。但Haxe在設計時引入了函數式編程的概念,因此Haxe中的大部分語法元素其實都是一種表達式,可以代表某個值。

// It is evaluated as the last expression inside it.
// Here is an example that use try-catch expression together with block expressions.
var result =
  try {
    var a = computationThatMayThrow();
    finalComputation(a);
  } catch (exception:Dynamic) {
    rollBack();
    defaultValue();
  }

另一個不同點表現在兩者處理分號的不同。TypeScript會自動在行末添加分號,因此不強制開發者使用分號。但這在Andy看來是一種 極端錯誤 的做法,它可能會帶來意料之外的后果。而Haxe強制要求開發者在行末使用分號。

其它的一些語法差別還包括:

  • 兩者都支持可選參數與剩余參數,其語法有細微的差別。
  • TypeScript支持ES6中的箭頭函數表達式,而Haxe的創造者 Nicolas Cannasse 則認為Haxe本身就具有表達式的特性,并且可以通過使用宏的Haxe類庫實現相同、甚至更簡潔的語法。
  • 兩者對于函數返回類型的定義方式不同,TypeScript使用的語法與箭頭函數相類似,而Andy認為Haxe的語法有 令人誤解之虞
  • 兩者在類與接口的定義語法上略有不同,TypeScript提供了一種名為“參數屬性”的語法糖以簡化類的編寫,而Haxe則需要通過宏才能夠實現相同的語法。
  • 兩者定義屬性的getter與setter的語法截然不同,TypeScript的方式較為直觀,而Haxe的語法則相當獨特。

Andy認為總的來說,兩者的語法具有高度的相似性,TypeScript本身提供了許多用以簡化代碼的語法,而Haxe則可通過宏進行擴展,使特定表達式的語義產生轉變。

語義

雖說TypeScript與Haxe在語法上非常近似,但它們在語義方面有著相當大的差距。首先兩者對于變量的范圍有著完全不同的定義,TypeScript的變量范圍與JS相同,通過var定義的變量作用于整個函數,而Haxe的變量范圍與現代語言更為接近,變量的范圍作用于一個代碼塊。

// TypeScript
{
  var a = 1;
}
console.log(a); // ok, because `a` exist outside of a block
// Haxe
{
  var a = 1;
}
trace(a); // error: Unknown identifier : a

在Andy看來,塊范圍的做法更適合于塊結構的語言。實際上,JavaScript的作者 Brendan Eich 也承認,當初選擇以函數作為變量范圍的做法是因為 時間緊迫

其它一些語義方面的差別還包括:

  • TypeScript對于this的處理方式與JS完全相同,在函數中的this具體指代的對象是動態決定的。而Haxe的作法與主流編程語言相似,this總是指向當前類的某個實例。
  • 兩種語言都定義了枚舉類型,但TypeScript中的枚舉只是一組有限值的集合,而Haxe中的枚舉是一種強大的函數式編程結構,稱為 泛代數數據類型(GADT) 。它更像是一種類型的有限集合,具有更為強大的處理方式。
  • 在switch語句方面,TypeScript的處理方式與傳統的C風格相同。而Haxe的switch實際上實現了函數式編程中的 模式匹配 。但 TypeScript支持ES6中的解構賦值特性 ,能夠一定程度上代替模式匹配的作用。

Andy總結道,TypeScript選擇盡可能遵循JS標準的語義,因此如今的JS開發者能夠很容易上手。而Haxe的做法是盡可能修復 JS的設計缺陷 ,遵循當今主流語言的設計方式,因此對于不具備JS開發經驗的用戶更具吸引力,并且Haxe中引入了部分函數式編程的概念,它很好地與類似于JS的語法相容。

類型系統

TypeScript與Haxe的基本類型非常接近,后者只是多了一個Int類型。兩者都支持自定義類型,TypeScript中可以使用類/接口及枚舉,而Haxe中還多了typedef與abstract兩個選擇。

以下是Andy對兩者的類型系統進行的一些具體比較:

  • TypeScript使用了一種結構化的系統,所有類型都可以被表達為某個接口,只要是具有相同屬性的類型都可以兼容。而Haxe的類型較嚴格,無法將匿名對象賦給某個類型化的變量。
  • 在Haxe中,Bool/Float/Int之間不可進行隱式轉換,除非使用 abstract
  • 兩者都支持編譯時類型推斷,但Haxe要更強大一些,它能夠從某個var變量首次使用時的賦值推斷出其類型,而不僅僅限于其初始化時的值。
  • TypeScript對于靜態類型采取了比較寬容的態度,即使在代碼中出現類型錯誤,依然能夠編譯為JS代碼。而Haxe會直接報錯,除非使用untyped關鍵字。
  • TypeScript中有一些不嚴謹(但具有一定實用性)的設計。例如函數參數本應是逆變的,但 在TypeScript中卻是雙變的

Andy總結說,Haxe的類型系統比TypeScript更為嚴格,但后者有時能夠簡化我們的工作。不過在他看來,如果出現了明顯的類型問題,應當中止編譯過程。Andy認為這種寬容會造成開發者的濫用,這方面的一個例子就是IE,它對語法錯誤的寬容讓開發者逐漸地忽視了語法錯誤與Web標準。而TypeScript很不幸地繼承了微軟的這一傳統……

代碼組織與生成

TypeScript的代碼組織方式與JS一樣靈活,在一個.ts文件中可以包含語句、函數與類的定義,還可以使用三種 模塊系統 ,可以導出變量、函數與類型。

Haxe的代碼組織方式更像靜態語言,不允許在頂層出現表達式(包括函數),只能包含在類型(主要是類)中。Haxe程序的入口方法是某個用戶指定類中的靜態main函數。

在文件組織上,TypeScript可以將.ts文件放置在任意位置上。而Haxe的做法類似于Java,其文件結構必須對應于包與模塊的層次。

在代碼生成時,Haxe能夠進行較多的高級優化功能,例如刪除不可訪問的代碼,對函數進行內聯等等。而TypeScript中的優化功能比較單一,主要是將一些高級語法特性轉譯為ES3/5所支持的JS語法。

Andy在總結中表示,從代碼的組織與生成來說,TypeScript表現出較高的靈活性,更接近于JS。而Haxe借鑒了主流編譯型語言的經驗,對于文件目錄要求較嚴格。

結論

在文章的最后,Andy對他的觀點進行了一番總結。他認為這兩門語言具有一定的相似性,但在設計思想上有許多不同之處。TypeScript總體更接近原生的JS,對于JS開發者來說更容易上手。而非JS開發者或許會喜歡Haxe,它更接近于現代的靜態語言,同時引入了許多函數式編程的概念,進一步加強了它的語法特性。

Andy個人更偏向于使用Haxe,除了因為他本身就是Haxe Foundation的一員之外,還因為Haxe能夠提供編譯為其它語言的功能,而這一點是TypeScript所不具備的,因為后者設計時就是為了編譯為JS這個目的而生。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!