Dart vs JavaScript vs TypeScript
2019-02-17
隨著Flutter受到開發者的重視,Google於2011年推出的Dart又重新進入大家關注的視野之內,不過除了Flutter以外,其實Google的開發者早在2016年也推出過Angular Dart,讓開發者以Dart開發網站應用,不過由於Angular Dart對比TypeScript版Angular文本長期不足,因此沒有引起太多關注。Google推出Flutter,可以說為大家對Dart的信心注入了一劑强心針,大家又重新開始關注這個已有8年歷史的程式語言。
本文想介紹的是,就是到底Dart有何特色?與JavaScript比較,又有何優劣?由於TypeScript開始於前端日漸盛行,我們亦可以趁機比較一下三種語言的異同。
應用範圍(Application)
有讀過上一篇Web Technology為何征服世界的朋友,應該已經知道JavaScript跟TypeScript已經可以在網頁、伺服器、智能電話、IOT、人工智能等範疇落地生根,Dart呢?托Flutter的發展,Dart已相當適合同時於網頁、伺服器、智能電話等範圍作開發,這一點與JavaScript已不相上下。不過Dart與JavaScript的最大分別,在於Dart本身可以有三種方法運行:分別是編譯為JavaScript(Compiled To JavaScript)、由Dart VM獨立運行(Standalone By Dart VM)、AOT編譯為機械碼(Ahead-of-time compiled to machine code). 而JavaScript現時最流行的做法,都是以Node運行JavaScript。Node運行JavaScript時,會將JavaScript轉變為機械碼(Machine Code),再運行機械碼。亦是因此Node JS的運行速度比其他動態語言如Python、Ruby等都較快。而Flutter所採用的,就是第三種方法,先編譯為機械碼(AOT Compilation)再在智能電話運行,因此大家可能聽過Flutter無需JS Bridge的原因,亦是由於早已編譯為機械碼,所以無需一個額外的JS Bridge再作轉譯。
React Native寫成的手機程式結構如下:
Flutter寫成的手機程式結講則如下:
基本運行
理解了Dart、JavaScript、TypeScript各自的應用範圍,就以三種語言都寫一個簡單的hello world為例:
JavaScript 和 TypeScript的例子都是:
console.log("Hello World");
Dart的例子:
print("Hello World");
要在command line運行,就分別如下面三行:
node index.js ts-node index.ts dart main.dart
以上index.js
、index.ts
、main.dart
都是進入點(entry point)。
具體分別不大,都是以語言的命令(node
,ts-node
,dart
)直接運行檔案,無須額外編譯,因為Dart受JavaScript 影響很大,想必也從JavaScript汲取了這一點。
順帶一提,JS及TS都支援REPL(Read-Eval-Print-Loop),但Dart無官方REPL,無法像JS及TS一樣於REPL裏面直接嘗試代碼。
基本語法
基本語法上,由於Dart受JavaScript影響很大,因此語法上很類似,但與TypeScript比較之下,卻看見了兩者取態的不同。TypeScript受Scala
的語法影響,將類別(Type)置於變數及參數之後;相反,Dart則沿用傳統Java及C#的前置類別語法。
由變數及函數一看,就可見不同之處。
變數(Variable)
JavaScript例子:
var num = 123; let hello = "Hello Dart!"; const obj = { key1: 1234, key2: 5678};
TypeScript例子(例子中類別皆可省略):
var num:number = 123; let hello:string = "Hello Dart!"; const obj:obj = { key1: 1234, key2: 5678};
Dart例子:
var num = 42; dynamic hello = "Hello Dart!"; final obj = {"key1":1234,"key":5678}; hello = 1234; String myName = "Tecky"; const PI = 3.141592654; var x; // == null
JavaScript的例子與TypeScript除了額外的類別,幾乎一模一樣,反之Dart則非常不同,Dart有幾種不同的方法定義變數:
- var: 定義一個新的變數,類別由數值所推論,所以如果將num重新賦值為String會出現Error,但賦值為同一類別則無問題。
- dynamic: 定義一個新的變數,類別不定,重新賦值任何類別都無問題。
- final: 定義一個新的常數,不可重新賦值。
- 直接前置類別如String: 定義一個新的變數,類別由前置類別決定,如果將重新賦值為其他類別會出現Error,但賦值為同一類別則無問題
- const: 與final類似,編譯常數,不可重新賦值。
可見Dart為了增强JavaScript類別上的欠缺,特意下了不少功夫,也解決了var
及let
之分,可是相較起來,似乎TypeScript的後置類別更為簡潔。
運算符號(Operators)
運算符號上,Dart跟JavaScript及TypeScript都很類似,其中一個最大的分別,在於Dart不支援===
,而且在if statement
中,必須使用boolean
,不可以如JS及TS一樣使用其他數值作boolean使用。
JavaScript 及TypeScript例子:
let num = null; if(!num){ console.log(`null is false`); } let zero = 0; if(!zero){ console.log(`zero is false`); }
以上用法在Dart是錯誤。只能使用Boolean作真假判斷 Dart例子:
var num = null; if(num == null){ print(`null is not false`); } var zero = 0; if(zero == 0){ print(`zero is not false`); }
函數(Function)
函數方面,Dart不使用關鍵字function
,語法較簡潔。
JavaScript例子:
function sum(a,b){ return a + b; }
TypeScript例子(例子中回傳值的類別可省略):
function sum(a:number,b:number):number{ return a + b; }
Dart例子:
sum(int a, int b){ return a+b; }
Dart的例子中,如果不寫int
的類別,那兩個參數都會是dynamic
類別,也就是任何類別都可以成為參數。
sum(a,b){ return a+b; } // equals to sum(dynamic a,dynamic b){ return a+b; }
而如果你寫了
sum("1",1);
就會出現一個Runtime Exception,也就是與JavaScript類似,失去了類別的保護作用。
似乎dynamic
就是dart
的any
吧。
而且dart還有一個特點,就是每個作為進入點的檔案都必須有一個名為main
的函數作為進入點,這一點明顯繼承了C
的傳統
main(){ sum(2,2); } sum(int a, int b){ return a+b; }
進階語法
除了以上基本函數、運算符號、變數的語法之外,Dart作為一種多用途語言,亦同時有類別(Class)的語法及專為非同步編程(Async Programming)而設的語法。
類別(Class)
類別是物件導向編程一個重要部份,Dart由JavaScript發展出來,因此類別語法亦與JavaScript非常類似,卻又與TypeScript有些相似。
以下是Dart的類別語法:
import 'dart:math'; class Point{ num x; num y; num z= 0; Point(num x, num y){ this.x = x; this.y = y; } num distanceTo(Point other) { var dx = x - other.x; var dy = y - other.y; return sqrt(dx * dx + dy * dy); } } void main(){ var p1 = Point(2,2); var p2 = new Point(4,5); p1.distanceTo(p2); }
與JavaScript相當類似,不同在於method
上定義的分別及初始化(initialization)物件不一定需要new
keyword.
以下是TypeScript同樣的類別語法:
class Point{ private x: number; private y: number; private z: number = 0; constructor(x:number,y:number){ this.x = x; this.y = y; } distanceTo(Point other){ let dx = x - other.x; let dy = y - other.y; return sqrt(dx * dx + dy * dy); } } const p1 = new Point(2,2); const p2 = new Point(4,5); p1.distanceTo(p2);
要留意的是,Dart亦不支援Access Modifier
,例如上面TypeScript所用的private
在Dart裏面不會用到。因此Dart
不能像
TypeScript
一樣,定義private field
.
非同步編程(Asynchronous Programming)
作為前端開發的程式語言,非同步編程是必不可少的一部份,汲取了JavaScript的教訓,Dart本身就支援了async
。
Dart的async
與JavaScript幾乎一模一樣,分別只是先後次序寫法,以及必須寫下傳回值的類別,也就是Future
。
Future
的概念與JavaScript Promise
無任何分別,只是名字上的分別。Java
的非同步編程類別,也是稱為Future
。
Future main() async{ await someAsyncOperation(); }
以下就是JavaScript
的版本
async function main(){ await someAsyncOperation() }
總結
本文總結了一些Dart
與JavaScript
及TypeScript
的異同,亦因篇幅所限,未能詳盡解釋其他Dart
特別用法例如Mixins
、Callable Classes
等。筆者認為Dart
明顯是希望改善JavaScript
弱類別的缺點而設計,然而Dart
卻因為多年的停滯錯過了前端近年的飛速發展,反而被後上的TypeScript
超越。程式語言的受歡迎程度,往往可以受一兩個應用範圍所左右:Objective-C
一直寂寂無名,因為iOS
的發展變得廣受歡迎;Rust
是Firefox
自家製程式語言,也因為Firefox
新引擎Servo
而變得聲名大噪,未知Flutter
能否為Dart
迎來發展之第二春呢?這就要大家拭目以待了。
留言
閱讀更多
學術探討系列:型別推論(一) Type inference(I)
2018-11-08
本篇是學術探討系列的第一篇,與先前不同的是內容上會以純粹學術角度集中探討一些題目,打響頭炮的將會是關於型別推論(Type inference)。歡迎大家留言建議一些題目啊。
到底React Hooks 有何特別?
2018-11-27
新近推出的React 16.7包括一個很有趣的功能,名字叫做React Hooks。看到這個名字,很多人會下意識認為是在講componentDidMount, componentDidUpdate等方法。但其實這些方法的正名是 React Lifecycle Method, 推出React Hooks是為了方便開發者多用functional component,但仍然能夠使用state及 props等重要功能。
到底React Hooks有何特別(二)?淺談useEffect及useReducer
2018-11-29
於本篇文章的上集,我們討論了useState如何令Stateful React Component簡化良多,此篇主要討論的是如何使 用useEffect。useEffect可以簡化state,很多人都提到React Hooks有可能可以完全取代Redux作為 React State Management的標準,正因如此。
Web Technology為何征服世界?
2019-02-05
2007年,蘋果宣佈發佈第一代iPhone,標誌智能電話時代的開始;一年之後Android亦宣告面世,從此時起,智能電話的發展迅速,Mobile App成為軟件的代 名詞,筆者初初成為軟件工程師時,總有朋友詢問我是否正在開發Mobile App,縱使筆者的專業一直都是網頁及後端開發之上。而其時亦有不少預測,預測[網站將會被Mobile App完全取代](https://searchenginewatch.com/sew/opinion/2414336/the-final-hurdle-is-cleared-apps-will-replace-websites)。網站所用的HTML、CSS、JS等,亦將成為歷史,送入博物館之內。