分享8个可以编译为JavaScript的语言

JavaScript 并不是最好的语言,特别是在复杂的应用中,它可能不太能胜任。为了避免这种情况,一些新的语言或现有语言的编译器被创造出来,不用写一行 JavaScript 或者考虑这种语言的局限,就能生产在浏览器能运行的代码。

本文介绍几种能够编译为 JavaScript 的语言,可以在浏览器或者 Node.js 中执行。

从代码简洁的角度考虑的话,个人还是比较喜欢 ClojureScript ,可以用简短的代码实现复杂的逻辑。

而想尝试 TypeScript 来开发最新的项目,是因为看到报道说在谷歌到处可以看到 TypeScript 的身影,也想体验下 TypeScript 项目开发。

1. TypeScript

官方网站:Get started with TypeScript

TypeScript 是一种由微软开发的自由和开源的编程语言,是 JavaScript 的超集;一个有效的 JavaScript 项目也是一个有效的 TypeScript 项目只是添加了静态类型。编译器也可以作为 ES2015+到当前实现的转译器,这样你总是能得到最新的特性。

不同于其他语言,TypeScript 保持了 JavaScript 完整的精神,只是此外添加了增加代码可靠性的功能。这些功能就是类型注释和其他类型相关的功能,得益于专业工具像是静态分析器和其他工具在重构过程的加入,这些功能使写 JavaScript 更加有趣。并且,类型的加入改善了你的应用不同组件之间的接口。

类型诊断是支持性的,你不必从一开始就写所有的类型。你可以先快速的写代码,然后再加入类型来让代码更稳定。

TypeScript 同样也支持高级类型,像是交叉类型,联合类型,类型别名,可辨识联合和类型保护。可以在 TypeScript 网站的 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})`;
    }
}

2. ClojureScript

官方网站:Get started with ClojureScript

ClojureScript 是将 Clojure 编程语言转换为 JavaScript 的编译器。它是一种通用的函数式语言,支持动态类型和不可变数据结构。

它是这个列表中唯一属于 Lisp 家族系列的编程语言,当然,它拥有很多功能。 例如,代码可以被视为数据,并且支持宏系统,这使得元编程技术成为可能。与其他 Lisps 不同的是,Clojure 支持不可变的数据结构,使副作用的管理更加容易。

对于新手来说,它的语法使用括号可能看起来很恐怖,但是这样做有着深刻的意义,从长远来看,你一定会感谢这种做法。语法上的简约性和语法抽象能力使 Lisp 成为解决那些需要高抽象层次的问题的强大工具。

虽然 Clojure 主要是函数式语言,但它不像 PureScript 或 Elm 那样纯粹;副作用仍然可以发生,但其他函数式特征仍然存在。

ClojureScript 使用 Google Closure 进行代码优化,并且与现有的 JavaScript 库兼容。

(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)

3. Dart

官方网站:Get started with 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;
}

4. Elm

官方网站:Get Started with 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

5. CoffeeScript

官方网站:Get Started with CoffeeScript

CoffeeScript 是一个旨在暴露 JavaScript 的精华并提供一个干净的语法并在合适地方保留语义的语言。虽然近年来这个语言的热度在下降,它正在改变方向并且现在有一个新的大版本支持 ES2015+特性。

你用 CoffeeScript 写的代码直接转化为可读的 Javascript 代码并且兼容现有的库。从 2 版本开始,编译器会产生兼容最新版本的 ECMAScript 的代码,比如,每次你使用 class,你就在 Javascript 中得到 class。并且,如果你使用 React,好消息是,JSX 兼容 CoffeeScript。

这个编译器有一个十分有特色的功能是有能力处理用 literate style 写的代码。literate style 相比于强调代码而把注释作为添加这种方式,而是你需要在一开始就写注释,代码只是偶尔出现。这种写代码的方式由 Donald Knuth 推荐,使得一个代码文件非常像一个技术文档。

相比于其他语言,CoffeeScript 代码可以在浏览器中用一个库直接执行。所以如果你想要写一个快速测试,你可以写你的代码在一个 text/coffeescriptscript 标签中,并且引入编译器,这样就可以把你的代码轻易的转化为 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)

6. PureScript

官方网站:Get Started with PureScript

PureScript 是由 Phil Freeman 创建的纯函数式强类型的编程语言。它旨在给可用的 JavaScript 库提供强大的兼容性,在精神上类似于 Haskell,但保持 JavaScript 的核心。

PureScript 的一个强项是它的极简主义。它不包括在其他语言中被认为是必需的功能的任何库。例如,不是编译器本身包含生成器和 promises,而是你可以使用特定的库来完成任务。你可以为所需功能选择想要的实现,这样可以在使用 PureScript 时实现高效和个性化的体验,同时保持生成的代码尽可能小。

其编译器的另一个显著特征就是能够在保持与 JavaScript 的兼容性的同时, 用库和工具生成整洁和可读的代码。

像其他语言一样,PureScript 有自己的构建工具叫做 Pulp,可以与 Gulp 进行比较, 但是用于以这种语言编写的项目。

关于类型系统,与 Elm 不同,即另一种 ML 式的语言,PureScript 支持高级类型的功能,如取自 Haskell 的 higher-kinded types(高级类类型) 以及 type classes(类型类), 从而允许创建复杂的抽象。

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"
        }
      ]

7. Scala.js

官方网站:Get Started with 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()

8. Reason

官方网站:Get Started with 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};

总结

介绍了这么多可以编译为 JavaScript 的语言,并不是说要放弃 JavaScript,现在主流前端开发是使用 TypeScript,JavaScript 现在也变得越来越好,不管哪种语言,适合的才是最好的。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/da976fdba0d21d5a0dcc6e9c6
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券