写点什么

TypeScript 与 Haxe:两种截然不同的 JS 转译工具横向对比

  • 2015-08-10
  • 本文字数:3266 字

    阅读完需:约 11 分钟

JavaScript 无疑是当今最火爆的编程语言之一,它的崛起要归功于 AJAX、Node.js 的出现以及时下各种 MVC 框架的流行。但作为一门在十天之内创建出来的语言,JS 本身存在着一些不完善之处、以及容易令人疑惑的地方,例如不支持强类型以及 new 关键字的用法。尤其与当前主流的面向对象语言以及动态语言相比,其不足之处显得尤为突出。

为了克服 JS 语言中的缺陷,让更多的人能够编写出优秀的代码,市面上出现了大量能够将其它编程语言转译为JS 的工具。其中较知名的有 CoffeeScript Dart GWT Script#,以及本文的主角 TypeScript Haxe

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, becauseaexist 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 这个目的而生。


感谢魏星对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

2015-08-10 19:374098
用户头像

发布了 428 篇内容, 共 171.9 次阅读, 收获喜欢 38 次。

关注

评论

发布
暂无评论
发现更多内容

基于多维数据库的全面预算软件,实现企业管理数字孪生

智达方通

企业管理 多维数据库 全面预算 全面预算软件

客户案例 | 博睿数据全面保障昆仑银行业务稳定性

博睿数据

Go未来演进:基于共同目标和数据驱动的决策

Tony Bai

golang 编程语言 Go 语言 gopher

极狐GitLab CI/CD 变量黑魔法之预定义变量

极狐GitLab

DevOps 镜像 CI/CD pipeline

从React Hooks看React的本质

canonical

前端 低代码 React

5.5V 至 36V 输入 3A 500kHz 降压转换器

二哈侠

【写作训练营打卡|05】 什么是 Web3?

写作 web3

Azure AI Studio使用体验——企业级AI工厂

Bruce Talk

AI Azure AI Studio Azure AI

软件测试/人工智能|Java Edit Plus 安装与配置指南

霍格沃兹测试开发学社

C 语言入门:如何编写 Hello World

小万哥

c 编程 程序员 后端 软件开发

写代码还是写标书

escray

技术人写作 21 天技术人写作行动营 21 天

数据驱动构建企业动态全面预算管理架构

智达方通

数据驱动 全面预算管理

火山引擎边缘计算用硬核助力赛事直播

火山引擎边缘云

边缘计算 直播 边缘云 边缘云原生

人人都能用的AI编程助手 CodeGeeX

凌览

AI 前端 后端 AIGC

Kafka Broker开小差了~

腾讯云混沌演练平台

kafka 混沌工程

项目经验分享:机器学习在智能风控中的应用

YoLo

AI

浅谈接口自动化测试

QE_LAB

接口自动化测试

app开发

Geek_8da502

软件测试/人工智能|Python数据可视化神器pyecharts教程(一)

霍格沃兹测试开发学社

Zookeeper数据存储源码剖析

极客罗杰

.h 头文件 | AI工程化部署

AIWeker

c AI工程化部署

简单之道

Tony Bai

golang 设计 Google Rob Pike Go 语言

30 | 图的表示:如何存储微博、微信等社交网络中的好友关系

鲁米

11 月 NFT 动态:交易量增长,Blur 地位加强

Footprint Analytics

区块链游戏 NFT

世界第一!蚂蚁图计算TuGraph打破LDBC SNB-BI世界纪录

TuGraphAnalytics

文心一言 VS 讯飞星火 VS chatgpt (154)-- 算法导论12.3 1题

福大大架构师每日一题

福大大架构师每日一题

聊点写标题那点破事儿

6点无痛早起学习的和尚

写作 21 天技术人写作行动营 标题

软件测试/人工智能|Python数据可视化神器pyecharts教程(二)

霍格沃兹测试开发学社

软件测试/人工智能|Python数据可视化神器pyecharts教程(三)

霍格沃兹测试开发学社

如何写好一份架构设计评审文档

童子龙

基础架构

Databend 开源周报第 123 期

Databend

TypeScript与Haxe:两种截然不同的JS转译工具横向对比_JavaScript_邵思华_InfoQ精选文章