Skip to content

编译到 JavaScript 技术路线深度总结报告 #17

@htoooth

Description

@htoooth

下面是一份基于你刚才系统性探索的编译到 JavaScript 技术路线深度总结报告。我不会简单罗列,而是帮你把这些语言和架构抽象成几种“根本范式”,并给出工程决策层面的结论。


一、问题本质:编译到 JS 的三种核心矛盾

所有这些语言,本质都在解决三个冲突:

1. 语义 vs JS 表达能力

  • 源语言通常更强(类型、并发、ADT 等)
  • JS 是动态 + 单线程 + 原型模型

2. 优化 vs 语义完整

  • 想优化 → 必须“压平语义”
  • 想完整语义 → 必须 runtime

3. 通用性 vs 工程复杂度

  • 通用 IR → 强但复杂
  • 专用语言 → 简单但不通用

二、六大技术路线(统一抽象)

你看到的所有语言,可以压缩成 6种范式


① 语言收缩型(ReScript / Gleam)

核心思想

限制语言 → 直接映射 JS / VM

Pipeline

语言(受限) → Typed AST → JS

特征

  • 几乎无 runtime
  • 无复杂 IR
  • JS 质量极高

优点

  • 性能好
  • 代码接近手写 JS
  • 实现简单

缺点

  • 不通用
  • 语言能力受限

② 语义映射型(Fable / 部分 Nim)

核心思想

强前端语义 → 映射到 JS

Pipeline

语言 → Typed AST → 语义转换 → JS

特征

  • 轻 IR(甚至无 IR)
  • 中等 runtime
  • 强类型系统依赖前端

优点

  • 实用
  • 开发效率高
  • JS interop 好

缺点

  • 优化能力有限
  • 难做跨语言统一

③ IR lowering 型(Kotlin / Scala.js / MoonBit)

核心思想

语义 → IR → lowering → JS

Pipeline

语言 → Typed AST → IR → lowering → JS

特征

  • IR 是核心资产
  • 可做优化
  • runtime 中等

优点

  • 可扩展
  • 可优化
  • 支持复杂语言特性

缺点

  • 工程复杂度高
  • JS 质量不一定最优

④ IR + Linker 型(Scala.js)

核心思想

IR + 全局分析(whole-program optimization)

Pipeline

IR → Linker → 优化 → JS

特征

  • 模块级 IR
  • call graph / DCE / inline
  • 类似 LTO

优点

  • 工业级优化
  • bundle size 控制强
  • 适合大型系统

缺点

  • 极高复杂度
  • 编译时间长

⑤ runtime 模拟型(Go / ClojureScript)

核心思想

在 JS 上实现语言运行时

Pipeline

语言 → 转换 → JS + runtime

特征

  • runtime 很重
  • JS 只是“宿主”
  • IR 不重要

优点

  • 语义完整
  • 语言能力不受限

缺点

  • 性能差
  • bundle 巨大
  • 不 JS-native

⑥ AST 多后端型(Haxe / Nim)

核心思想

AST 即 IR,多后端 codegen

Pipeline

语言 → Typed AST → 多 backend

特征

  • 无 SSA
  • 无强 IR
  • 多目标语言

优点

  • 实现简单
  • 多平台支持好

缺点

  • 优化能力弱
  • 难做复杂分析

三、统一对比矩阵(核心)

路线 IR runtime JS质量 优化 通用性 复杂度
ReScript 极低 ★★★★★ ★★
Fable ★★★★ ★★ ★★
Kotlin ★★★ ★★★ ★★★
Scala.js 强+Linker ★★★ ★★★★★ ★★★★★
Go 极高 ★★★
ClojureScript 极高 ★★★
Haxe ★★★ ★★ ★★
Nim ★★ ★★ ★★
MoonBit ★★★★ ★★★★ ★★★★

四、关键设计维度(你必须做的选择)

你现在真正要决定的是这几个“轴”:


1. IR 是否是核心?

选 YES(MoonBit / Scala.js)

  • 优点:可扩展、多语言
  • 缺点:复杂

选 NO(ReScript / Fable)

  • 优点:简单
  • 缺点:不可扩展

2. runtime 要多重?

无 runtime(ReScript)

→ 最 JS-native

中 runtime(Kotlin / Fable)

→ 平衡

重 runtime(Go / CLJS)

→ 语义完整


3. 是否做 Linker?

无 linker

→ 小项目 OK

有 linker(Scala.js)

→ 工业级系统


4. 是否支持多语言?

单语言(Kotlin / MoonBit)

→ 更好优化

多语言 IR(你目标)

→ 极高复杂度


五、最重要的工程洞察(精华)


1. IR 的价值不在“翻译”,而在“统一优化空间”

没有 IR:

  • 每种语言都要单独处理

有 IR:

  • 优化只写一次

2. runtime 和 IR 是对冲关系

IR 强 runtime 弱
IR 弱 runtime 强

3. JS backend 的难点不在“生成代码”,而在:

  • CFG 重建
  • phi 消除
  • 类型稳定
  • object shape
  • JIT 友好

4. “语言设计”比“编译器设计”更重要

ReScript 成功的原因:

不是编译器强,而是语言被限制得刚好


5. Linker 是规模化的关键

没有 linker:

  • bundle 会膨胀
  • 优化受限

6. 大部分系统失败在 runtime

  • GopherJS → 太重
  • ClojureScript → 不 JS-native

六、你的目标定位(关键判断)

你现在的目标是:

多语言 → JS

这意味着:


你不能选的路线

❌ ReScript

→ 不支持多语言

❌ Go / CLJS

→ runtime 爆炸

❌ Haxe / Nim

→ 优化不足


你真正的候选路线

方案 A(推荐)

Typed AST → SSA IR → JS backend

方案 B(进阶)

Typed AST → SSA IR → Linker → JS

七、最终架构建议(最重要)

结合所有语言的最佳实践,你的系统应该是:


1. 前端层(多语言)

Language A → Typed ASTLanguage B → Typed AST

2. IR 层(核心)

Typed AST → SSA IR(统一)

特征:

  • block arguments(代替 phi)
  • 强类型
  • CFG 明确
  • JS-friendly

3. 优化层

SSA IR → optimization passes
  • constant folding
  • DCE
  • inline
  • CFG simplify

4. Linker(可选但推荐)

Modules → call graph → DCE → bundle

5. Backend

SSA IR → JS AST → JS

重点:

  • CFG 重建
  • 结构化代码
  • JIT-friendly 输出

6. runtime(最小化)

只包含:

  • helper functions
  • minimal polyfill

八、一句话最终结论

所有“编译到 JS”的技术路线,本质是在 IR、runtime、语言约束之间做权衡;如果你的目标是多语言统一编译平台,唯一可扩展的路径是“SSA IR + 可选 Linker + 最小 runtime”,并结合 ReScript 的“JS-friendly 代码生成策略”。


九、如果继续深入

下一步最有价值的不是再看语言,而是:

👉 把你的 IR 写出来

我可以帮你直接落到:

  • IR spec(type + instruction)
  • 示例 lowering(if / loop / class / closure)
  • JS backend mapping
  • Linker 设计(call graph + DCE)

这一步会把你从“理解架构”推进到“真正能做系统”。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions