10個編譯為JavaScript的語言
現代應用相比普通的網頁有ofollow,noindex" target="_blank">不同的要求 。但是瀏覽器是一個有著一套(大部分)固定可用的技術的平臺,JavaScript依然是web應用的核心語言;任何需要在瀏覽器上跑的應用都需要使用這種語言。
我們都知道Javascript並不是最好的語言,特別是在複雜的應用中,它可能不太能勝任。為了避免這種情況,一些新的語言或現有語言的編譯器被創造出來,你不用寫一行Javascript或者考慮這種語言的侷限,就能生產在瀏覽器能執行的程式碼。
這篇文章包括了十種有趣的語言能夠編譯為Javascript,在瀏覽器或者Node.js中被執行。
--ADVERTISEMENT--
Dart
Dart是一個典型的面向物件的語言,任何東西都是一個物件並且任何物件都是一個類的例項(物件也可以表現為函式)。它的特殊性用於打造面向瀏覽器,伺服器和移動裝置的應用。 它由谷歌來維護,是用於驅動下一代的AdWords UI。AdWords UI是谷歌盈利的重要產品,這也證明了它在體量上的強大。
這種語言可以編譯為JavaScript用於瀏覽器,或者直接通過Dart VM解釋,這樣也可以允許你構建服務端應用。移動應用可以通過Flutter SDK建立。
複雜的應用還需要一系列特別為任務所設計的成熟的庫和語言特性,Dart這些都有。舉例來說一個流行的庫是AngularDart ,一個Dart版本的Angular。
它允許你寫非侵入式的型別安全的程式碼,但是這不是必須的,因為他們可以自動檢測型別。它可以允許你快速構建原型而不用過於思考細節,一旦你需要的時候,你可以加入型別讓它更健壯。
至於在VM中的併發程式設計,相比與共享記憶體執行緒(Dart是單執行緒的),Dart使用所謂的Isolates ,有它自己的堆記憶體,而交流是通過傳遞資訊。在瀏覽器上,情況就有點不一樣了:相比與建立一個新的isolates ,你建立一個新的Workers 。
// Example extracted from dartlang.org import 'dart:async'; import 'dart:math' show Random; main() async { print('Compute π using the Monte Carlo method.'); await for (var estimate in computePi()) { print('π ≅ $estimate'); } } /// Generates a stream of increasingly accurate estimates of π. Stream<double> computePi({int batch: 1000000}) async* { var total = 0; var count = 0; while (true) { var points = generateRandom().take(batch); var inside = points.where((p) => p.isInsideUnitCircle); total += batch; count += inside.length; var ratio = count / total; // Area of a circle is A = π⋅r², therefore π = A/r². // So, when given random points with x ∈ <0,1>, // y ∈ <0,1>, the ratio of those inside a unit circle // should approach π / 4\. Therefore, the value of π // should be: yield ratio * 4; } } Iterable<Point> generateRandom([int seed]) sync* { final random = new Random(seed); while (true) { yield new Point(random.nextDouble(), random.nextDouble()); } } class Point { final double x, y; const Point(this.x, this.y); bool get isInsideUnitCircle => x * x + y * y <= 1; }
TypeScript
TypeScript 是Javascript的超集;一個有效的Javascript專案也是一個有效的TypeScript專案只是添加了靜態型別。編譯器也可以作為ES2015+到當前實現的轉譯器,這樣你總是能得到最新的特性。
不同於其他語言,TypeScript保持了Javascript完整的精神,只是此外添加了增加程式碼可靠性的功能。這些功能就是型別註釋和其他型別相關的功能,得益於專業工具像是靜態分析器和其他工具在重構過程的加入,這些功能使寫Javascript更加有趣。並且,型別的加入改善了你的應用不同元件之間的介面。
型別診斷是支援性的,你不必從一開始就寫所有的型別。你可以先快速的寫程式碼,然後再加入型別來讓程式碼更穩定。
TypeScript同樣也支援高階型別,像是交叉型別,聯合型別,類型別名,可辨識聯合和型別保護。你可以在TypeScript Documentation 網站的Advanced Types 頁面檢視。
如果你使用React的話,通過新增React型別,JSX也是支援的。
class Person { private name: string; private age: number; private salary: number; constructor(name: string, age: number, salary: number) { this.name = name; this.age = age; this.salary = salary; } toString(): string { return `${this.name} (${this.age}) (${this.salary})`; } }
——
Elm
Elm是一個可以編譯成JS,HTML和JS的純函數語言程式設計語言。你可以只通過Elm建立一個完整的網站,這使得它是一個對像React這樣的Javascript框架的一個很好的代替。通過它建立的應用自動使用了虛擬DOM庫,使得它很快。一個大的加分項是內建的結構讓你忘記資料流而是關注於資料宣告和邏輯。
在Elm中,所有函式都是純粹的,這意味著他們總是對一個給予的輸入返回一個相同的輸出。T他們不能做其他任何事情,除非你指定。舉例來說,獲取一個遠端的API你會建立一個command 函式來通訊外部世界,和一個subscriptions 函式監聽回覆。另一個純粹的點是,值是不可變的,當你需要什麼的時候,你建立一個新值而不是改變它。
ELm的接受可以是平緩的;可以使用ports 來和Javascript或其他庫溝通。雖然Elm還沒有到達版本1,它已經用於複雜大型的應用了,這使得它對複雜應用是一個可行的解決方案。
ELm其中一個吸引人的功能是初學者友好的編譯器,它生成幫助你修復你的程式碼的資訊,而不是產生難以閱讀的資訊。如果你正在學習這門語言,編譯器本身就是一個大的幫助。
module Main exposing (..) import Html exposing (..) -- MAIN main : Program Never Model Msg main = Html.program { init = init , update = update , view = view , subscriptions = subscriptions } -- INIT type alias Model = String init : ( Model, Cmd Msg ) init = ( "Hello World!", Cmd.none ) -- UPDATE type Msg = DoNothing update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of DoNothing -> ( model, Cmd.none ) -- VIEW view : Model -> Html Msg view model = div [] [text model] -- SUBSCRIPTIONS subscriptions : Model -> Sub Msg subscriptions model = Sub.none
PureScript
PureScript 是一個純函式強型別的程式語言,由Phil Freeman創造。它旨在與現有的JavaScript庫進行很好的相容,與Haskell精神上類似,但是核心保留了Javascript。
PureScript一個重要的點是它的極簡主義。它沒有包含任何在其他語言認為很重要的功能庫。比如,相比於在編譯器中包含generators和promises,你可以自己使用指定的庫來完成這個任務。你可以選擇你想要的功能的實現,當使用PureScript的時候需要高效和個性化的經驗,能使生成的程式碼儘可能的小。
這個編譯器另一個重要的功能是構建出清晰可讀的程式碼,能夠相容Javascript包括庫和工具。
和其他語言一樣,PureScript有它自己的構建工具稱為Pulp,可以和Gulp做對比,只是用這個語言寫的。
至於型別系統,不同於另一個ML類的語言Elm,PureScript支援更先進的型別特性比如高階類型別 和型別類,這些是從Haskell來的特性,允許建立複雜的抽象。
module Main where import Prelude import Data.Foldable (fold) import TryPureScript main = render $ fold [ h1 (text "Try PureScript!") , p (text "Try out the examples below, or create your own!") , h2 (text "Examples") , list (map fromExample examples) ] where fromExample { title, gist } = link ("?gist=" <> gist) (text title) examples = [ { title: "Algebraic Data Types" , gist: "37c3c97f47a43f20c548" } , { title: "Loops" , gist: "cfdabdcd085d4ac3dc46" } , { title: "Operators" , gist: "3044550f29a7c5d3d0d0" } ]
CoffeeScript
CoffeeScript是一個旨在暴露JavaScript的精華並提供一個乾淨的語法並在合適地方保留語義的語言。雖然近年來這個語言的熱度在下降,它正在改變方向並且現在有一個新的大版本支援ES2015+特性。
你用CoffeeScript寫的程式碼直接轉化為可讀的Javascript程式碼並且相容現有的庫。從2版本開始,編譯器會產生相容最新版本的ECMAScript的程式碼,比如,每次你使用class
,你就在Javascript中得到class
。並且,如果你使用React,好訊息是,JSX相容CoffeeScript。
這個編譯器有一個十分有特色的功能是有能力處理用literate style
寫的程式碼。literate style
相比於強調程式碼而把註釋作為新增這種方式,而是你需要在一開始就寫註釋,程式碼只是偶爾出現。這種寫程式碼的方式由Donald Knuth推薦,使得一個程式碼檔案非常像一個技術文件。
相比於其他語言,CoffeeScript程式碼可以在瀏覽器中用一個庫直接執行。所以如果你想要寫一個快速測試,你可以寫你的程式碼在一個text/coffeescript
script標籤中,並且引入編譯器,這樣就可以把你的程式碼輕易的轉化為JavaScript了。
# Assignment: number= 42 opposite = true # Conditions: number = -42 if opposite # Functions: square = (x) -> x * x # Arrays: list = [1, 2, 3, 4, 5] # Objects: math = root:Math.sqrt square: square cube:(x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # Existence: alert "I knew it!" if elvis? # Array comprehensions: cubes = (math.cube num for num in list)
Get Started with CoffeeScript 2
ClojureScript
ClojureScript是一個轉化Clojure程式語言為JavaScript的編譯器。Clojure是一個多用途的函式式原因伴隨著動態型別和不可變資料結構的支援。
這是這個列表中唯一一個屬於Lisp家族的語言,自然有著它們共同的特性。舉例來說,程式碼可以作為資料,支援巨集系統,使得超程式設計成為可能。 Unlike other Lisps, Clojure has support for immutable data structures, making the management of side-effects easier.不同於其他類Lisp,Clojure支援不可變資料結構,使得函式副作用的管理更容易。
這個語法對初學者看起嚇人,因為圓括號的使用。但這樣使用是經過深思熟慮的,並且在長遠看來你一定會感謝這種語法的。語法的極簡和抽象能力使得Lisp成為一個解決高抽象問題的強力工具。
雖然Clojure主要是一個函式式語言,但是不像PureScript或者Elm那樣純粹。函式副作用還是會發生,但是其他函式式特性也會存在。
ClojureScript使用Google Closure做程式碼優化並且也相容現有的JavaScript庫。
; Extracted from https://github.com/clojure/clojurescript/blob/master/samples/dom/src/dom/test.cljs (ns dom.test (:require [clojure.browser.event :as event] [clojure.browser.dom:as dom])) (defn log [& args] (.log js/console (apply pr-str args))) (defn log-obj [obj] (.log js/console obj)) (defn log-listener-count [] (log "listener count: " (event/total-listener-count))) (def source(dom/get-element "source")) (def destination (dom/get-element "destination")) (dom/append source (dom/element "Testing me ") (dom/element "out!")) (def success-count (atom 0)) (log-listener-count) (event/listen source :click (fn [e] (let [i (swap! success-count inc) e (dom/element :li {:id "testing" :class "test me out please"} "It worked!")] (log-obj e) (log i) (dom/append destination e)))) (log-obj (dom/element "Text node")) (log-obj (dom/element :li)) (log-obj (dom/element :li {:class "foo"})) (log-obj (dom/element :li {:class "bar"} "text node")) (log-obj (dom/element [:ul [:li :li :li]])) (log-obj (dom/element :ul [:li :li :li])) (log-obj (dom/element :li {} [:ul {} [:li :li :li]])) (log-obj (dom/element [:li {:class "baz"} [:li {:class "quux"}]])) (log-obj source) (log-listener-count)
Get Started with ClojureScript
Scala.js
Scala.js是一個將Scala程式語言轉化為JavaScript的編譯器。Scala是一個旨在融合面向物件和函數語言程式設計兩種思想到一種語言,為了打造容易接受的強力的工具
作為一個強型別語言,你會從它部分型別推斷這種靈活的型別系統中受益。大部分的值會被推斷,但函式引數仍然需要明確的型別註釋。
雖然許多通常的面向物件模式都支援(比如任何值都是一個物件並且操作是一個方法呼叫),但你也有函式式特性比如一等函式和不可變資料結構。
Scala.js其中一個特殊的優勢是,你可以毫不費力的從你熟悉的面向物件開始向更函式式的轉移,以你自己的需要和步調。同樣的,現存的JavaScript程式碼和庫和你的Scala程式碼相容。
Scala的初學者會發現這個語言和JavaScript並沒有多大不同,對比下面兩個意思一樣的程式碼:
// JavaScript var xhr = new XMLHttpRequest(); xhr.open("GET", "https://api.twitter.com/1.1/search/" + "tweets.json?q=%23scalajs" ); xhr.onload = (e) => { if (xhr.status === 200) { var r = JSON.parse(xhr.responseText); $("#tweets").html(parseTweets(r)); } }; xhr.send();
// Scala.js val xhr = new XMLHttpRequest() xhr.open("GET", "https://api.twitter.com/1.1/search/" + "tweets.json?q=%23scalajs" ) xhr.onload = { (e: Event) => if (xhr.status == 200) { val r = JSON.parse(xhr.responseText) $("#tweets").html(parseTweets(r)) } } xhr.send()
Reason
Reason是一個由Facebook創造和維護的語言,它為OCaml編譯器提供了新的語法,並且程式碼可以轉換成JavaScript和原生程式碼。
作為ML家族的一部分並且自己本身是函式式語言,它天生提供了強大但是靈活的伴隨型別推斷的型別系統,代數資料型別和模式匹配。它也支援不可變資料型別和引數多型(也被其他語言稱為泛型),但是在OCaml中,也是支援面向物件程式設計的。
通過bucklescript 繫結就可以使用現存的JavaScript庫。你也可以在你的Reason程式碼旁邊混入你的JavaScript。插入的JavaScript程式碼不會嚴格的檢查,但作為快速修復和原因也是不錯的。
如果你是一個React開發者,繫結是可能的 ,並且這個語言也支援JSX。
/* A type variant being pattern matched */ let possiblyNullValue1 = None; let possiblyNullValue2 = Some "Hello@"; switch possiblyNullValue2 { | None => print_endline "Nothing to see here." | Some message => print_endline message }; /* Parametrized types */ type universityStudent = {gpa: float}; type response 'studentType = {status: int, student: 'studentType}; let result: response universityStudent = fetchDataFromServer (); /* A simple typed object */ type payload = Js.t {. name: string, age: int }; let obj1: payload = {"name": "John", "age": 30};
Haxe
Haxe是一個多正規化程式語言,並且它的編譯器可以產生二進位制或者其他語言的原始碼。
雖然Haxe提供了嚴格的型別系統並帶有型別推斷,它也可以作為動態語言只要目標語言支援。同樣的,它也支援多種的程式設計風格比如面向物件,泛型,函式式。
當你寫Haxe程式碼的時候,你可以為編譯指定多個平臺或語言,但不需要對程式碼做什麼大的改變。指定目標的程式碼塊也支援。
你可以用Haxe同時寫前端和後端用同樣的程式碼,並且通過Haxe Remoting 進行溝通,既可以同步連線也可以非同步連線。
不出所料,Haxe程式碼可以相容現有的庫但也提供了成熟的標準庫。
// Example extracted from http://code.haxe.org extern class Database { function new(); function getProperty<T>(property:Property<T>):T; function setProperty<T>(property:Property<T>, value:T):Void; } abstract Property<T>(String) { public inline function new(name) { this = name; } } class Main { static inline var PLAYER_NAME = new Property<String>("playerName"); static inline var PLAYER_LEVEL = new Property<Int>("playerLevel"); static function main() { var db = new Database(); var playerName = db.getProperty(PLAYER_NAME); trace(playerName.toUpperCase()); db.setProperty(PLAYER_LEVEL, 1); } }
Nim
Nim是一個靜態型別,多正規化程式語言,有著極簡風格與空格敏感的語法,編譯為C,C++和JavaScript。
這個語言本身很小,但它的超程式設計能力會吸引你自己去實現一些在別的語言內建的功能。這些構建模組有巨集,模板和泛型,通過它們你可以實現不論是簡單的功能還是不同的泛型。這使得Nim成為一個非常通用的語言可以適應你的需求,有著Lisp的精髓。
Nim的語法抽象功能允許你去讓語言去適應你的功能,讓真正的DSLs 成為可能。如果你有著專門的任務需要處理,你可以獲得更高階的表達性。
# Reverse a string proc reverse(s: string): string = result = "" for i in countdown(high(s), 0): result.add s[i] var str1 = "Reverse This!" echo "Reversed: ", reverse(str1) # Using templates template genType(name, fieldname: expr, fieldtype: typedesc) = type name = object fieldname: fieldtype genType(Test, foo, int) var x = Test(foo: 4566) echo(x.foo) # 4566
Conclusion
如果JavaScript不是你最喜歡的語言,你依然可以建立web應用而不用忍受這個技術的缺點。可供選擇的範圍很廣,從純粹的函式式語言,比如PureScript,到面嚮物件語言,比如Dart。並且如果你想要不只是語言的轉化,你也可以選擇比如Elm,Elm提供像是虛擬DOM和內建的架構這樣的工具。
你是否有嘗試了這篇文章的任何一種語言,又或者你有自己的推薦?請在評論中讓我知道!