10种能够编译成 JavaScript 的脚本语言

相比于简单的网站,现代应用具有不同的需求。但是浏览器是一个具有(几乎)固定可用技术集合的平台,而JavaScript依然是 web 应用的核心语言;需要运行在浏览器中的任何应用必须使用该语言来实现。

我们知道 JavaScript 并不是适用于所用任务的最佳语言,而且当遇到复杂的应用时, JavaScript 很可能会功亏一篑。为了避免这种问题,现在已经创建了一些新语言或是现有语言的转换编译器,他们能够生成可在浏览器中运行的代码,而无需编写一行代码,也无需考虑语言的局限性。

这篇文章包含了十种有趣语言的列表,这些语言可以转换编译为可在浏览器中或是类似Node.js平台上运行的JavaScript代码。

Dart

Dart 是一种典型的面向对象语言,其中的一切都是对象,而每一个对象都是类的一个实例(对象也可以作为函数)。它为构建浏览器,服务器与移动设备应用而特意设计的。它由 Google 维护,并且是驱动下一代 AdWords UI — Google 收入中的最重要产品– 的语言,也在一定程度上证明了其自身的强大。

Dart 可以转换为在浏览器中可用的 JavaScript ,或是直接由 Dart VM 解释,从而也允许你构建服务器应用。 使用 Flutter SDK 可以构建移动应用。

复杂应用同时需要成熟的库与特意为任务而设计的语言特性,而 Dart 包含有所有这些要素。一个流行库的例子就是 AngularDart, Angular 的 Dart 版本。

它允许你编译类型安全的代码而无需过于侵入;你可以编写类型,但是你并不会要求如此,因为他们可以推导类型。这使得快速原型成为可能,而无需深入思考细节,但是一旦你使得某些内容工作起来,你就可以添加类型使其更为稳健。

对于 VM 中的并行编译,不同于共享内存线程(Dart 是单线程),Dart使用所谓的分离(Isolates),具有其自己的内存堆,在其中使用消息实现通信。在浏览器中,故事有些不同:你创建新 Worker,而不是创建新 isolate。

// 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;
}

Dart 入门

TypeScript

TypeScript 是 JavaScript 的是一个超集;一个正确的 JavaScript 程序同时也是一个正确的 TypeScript程序,但是添加了静态类型。编译器同时可以作为 ES2015+ 到当前实现的转换编译器,从而你总是可以最新的特性。

不同于许多其他的语言,TypeScript保留了 JavaScript 的灵魂,仅添加了改进代码稳定性的特征。他们是类型注释以及类型相关的功能,多亏了静态分析器以及辅助重构过程的其他工具等特殊工具的使用,编写JavaScript代码变得更为愉悦。同时,类型的添加改进了我们应用的不同组件之间的接口。

类型推导也是支持的,所以你不必从头编写所有的类型。你可以编写快速的解决方案,然后添加所有的类型使得你的类型更为可信。

TypeScript 同时支持高级类型,例如交集类型,联合类型,类型别名,独立联合类型以及类型警卫。你可以在TypeScript 文档 网站中的高级类型 页面查看到这些内容。

如果你使用 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})`;
    }
}

TypeScript 入门

Elm

Elm 是一个可以编译为 JS,HTML 与 CSS 的纯函数式语言。你可以仅使用 Elm 构建一个完整的网站,将其作为 JavaScript框架,例如 React,的替代。你用其构建的应用会自动使用虚拟 DOM 库,从而使其非常快速。另一个优点就是内建的体系结构,从而使你忘记数据源,而专注于数据声明与逻辑。

在 Elm 中,所有的函数都是纯粹的,这意味着对于给定的输入他们总是返回相同的输出。他们不会做其他的任何事情,除非你显式指定。例如,要访问远程 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 [] 
-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none

Elm 入门

PureScript

PureScript 是一种纯函数式与强类型的编程语言,由 Phil Freeman 创建。 它致力于提供与现有的 JavaScript 库的强兼容性,在内在灵魂上类似于 Haskell , 但是保持 JavaScript 作为其核心。

PureScript 的优点就在于其极简主义。它并不包含任何功能性库,而在其他语言中被通常被认为是其核心。例如,在其编译器自身中并不包含生成器与 promises ,对于该任务你可以使用特定的库。你可以选择你需要的特性的实现,使用 PureScript 允许高效与个性化体验,同时使得生成的代码尽可能小。

其编译器的另一个独特特性就是生成简洁与可读代码的能力,同时保持与 JavaScript 的兼容,两者都专注于库与工具。

类似其他语言,PureScript具有其自己的构建工具,名为 Pulp, 相比于 Gulp,后者是使用该语言编写的工程。

对于类型系统—不同于 Elm, 另一个 ML-类语言—PureScript 可以支持高级类型特性,例如 higher-kinded 类型 与类型类,后者来自 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"
        }
      ]

PureScript 入门

CoffeeScript

CoffeeScript 是一门致力于展示 JavaScript 好的部分,同时提供简洁的语法并保持语义的编程语言。尽管该语言的流行度在最近几年有所减弱,它正在变换方向,并且出现支持 ES2015+ 特征的新版本。

你使用 CoffeScript 编写的代码直接被转换为可读的 JavaScript 代码,并且与现有的库保持兼容。由版本2开始,编译器将会生成与 ECMAScript 的最新版本兼容的代码,每次你使用 class ,你就会在 JavaScript 中得到一个 class 。同时,如果你使用 React,还有一个好消息:JSX 是与 CoffeeScript 兼容的。

编译器的一个非常显着的特征是能够处理以文字风格编写的代码,而不是强调代码并将注释作为额外的内容,你可以先写注释, 代码只是偶尔出现.这种编程风格,Donald Knuth推出了一个代码文件非常类似于一个技术文章。

与其他语言不同,CoffeeScript代码可以使用库直接在浏览器中解释。所以如果你想创建一个快速测试, 你可以在text/ CoffeeScript的脚本标签内编写代码,并包含编译器,将代码转换成JavaScript。

# 赋值:
number   = 42
opposite = true

# 条件:
number = -42 if opposite

# 函数:
square = (x) -> x * x

# 数组:
list = [1, 2, 3, 4, 5]

# 对象:
math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x

# Splats:
race = (winner, runners...) ->
  print winner, runners

# 存在性:
alert "I knew it!" if elvis?

# 数组 推导:
cubes = (math.cube num for num in list)

开始使用CoffeeScript2

读 Modern JavaScript

跟上不断变化的JavaScript世界

图1:10种能够编译成 JavaScript 的脚本语言

现在开始阅读

ClojureScript

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

它是这个列表中唯一属于Lisp系列的编程语言,而且它们共享了很多特性。例如,代码可以作为数据处理,并且可以使用宏系统,使元编程技术成为可能。与其他Lisps不同,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)

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

Scala.js 入门

Reason

Reason 是一门 Facebook 创建与维护的语言,为 OCaml 编译器提供了一种新语法,同时代码可以被同时转换为 JavaScript 与原生代码。

作为 ML 家庭的一员,同时自身是一门函数式语言,它提供了强大而灵活的带有推导的类型系统,代数数据类型以及模式匹配。它同时支持不可修改数据类型与参数化多态(在其他语言中被称为范型),但是在 OCaml 中,同时支持面向对象编程。

通过 bucklescript 绑定可以使用现有的 JavaScript 库。你可以混用 JavaScript与 Reason 代码。所插入的 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};

Reason 入门

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);
  }
}

Haxe 入门

Nim

Nim 是一门具有极简主义与空格敏感语法的静态类型,多范型编程语言,可以编译为 C, C++, 与 JavaScript。

语言本身很小巧,但是其元编程能力使得其对于你自己实现其他语言中的内建特性很有吸引力。实现该目的的构建块是宏,模板与范型,使用这些构建块你可以实现简单的特性到不同的范型。这使得 Nim 成为一门可以满足你需求的极其灵活的语言,具有 Lisp 的灵魂。

Nim 的句法抽象特征允许你为你的问题修改语言,使得 DSL 成为可能。如果你有特定的任务要解决,你可以得到更高级别的表达式。

# 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

Nim 入门

结论

如果JavaScript不是你最喜欢的语言,你仍然可以创建web应用程序而不必忍受技术的缺点。用于创建这些应用程序的语言 可以有更多的尝试, 从纯函数语言,如PureScript,到面向对象的语言,如Dart。如果你想要的不仅仅是一对一的语言翻译, 你可以选择像Elm一样的语言,为你提供工具,像一个虚拟的DOM和一个内置的架构。

你有没有试过这篇文章的任何语言,或者你有推荐的吗?可以在评论中告诉我们吧!

本文文字及图片出自 coyee.com

余下全文(1/3)
分享这篇文章:

请关注我们:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注