一个只会编译、永不解释的 JavaScript:零运行时,是惊喜还是妥协?

一个只会编译、永不解释的 JavaScript:零运行时,是惊喜还是妥协?

同样一段代码,一个要背着几十 MB 的引擎跑,另一个直接变成几 KB 的可执行文件——你选哪个?

一、JavaScript 的"重量",从不是代码本身

先做一个思想实验。你写了一句 console.log("hi"),它只有一个字节级的小脚本。可一旦你把它交给 Node 运行,背后其实拉起的是完整的一套生态:解析器、解释器、JIT 编译器、垃圾回收、标准库……这个"陪跑阵容"通常有几十 MB,甚至上百 MB。

问题就出在这里:JavaScript 的部署单位,从来不是你的代码,而是"你的代码 + 一个引擎"。 于是它带来一串连锁反应——

  • 冷启动慢:Serverless 每拉起一次函数,都要先把引擎和运行时"唤醒"一遍;
  • 体积膨胀:一个 10 行的 CLI 工具,打包发布动辄十几、几十 MB;
  • 分发困难:IoT 设备、嵌入式环境、游戏机这些资源受限的场景,直接装不下一个完整引擎。

过去,想解决这些问题的人,普遍选择"换一条赛道":改用 Rust、Go、C++——因为它们的产物就是"一个可执行文件",不背运行时。JavaScript 的优雅与轻便,似乎总要用"运行时"来偿还。

img-cost

二、Porffor:把"解释"这一步,从运行期挪到编译期

有一个项目,试图从根本上改写这笔"债"。它叫 Porffor(读作 poor-for),取自威尔士语,意思是"紫色"——作者 Oliver Medhurst 说,选这个名字是因为"没有哪个 JS 引擎是紫色的"。

它是一个 Ahead-of-Time(AOT)编译器:把 JavaScript 提前编译成 WebAssembly、C,或直接的原生二进制。关键在"提前"两个字——它不在运行期做解释或 JIT,而是在你构建时,就把 JS 变成真正能跑的机器码。

传统:JS 代码 → 运行期:解释 / JIT → 执行 Porffor:JS 代码 → 编译期:→ WebAssembly / C / 原生二进制 → 直接执行

和很多"JS 转 Wasm"工具最大的不同在于:那些工具是"把你的代码 + 一个迷你解释器"一起打包进 Wasm,所以产物依旧巨大;而 Porffor 是真编译,产物里只有你的代码需要的东西,零运行时、零解释器

这个项目很有意思的一点是它的"出身"。作者说它本来是业余时间做的个人项目,后来拿到资助才转为全职开发。他的 GitHub 主页上挂着一条曲线:Test262 通过率在拿到资助前后出现了明显拐点。"有了资金,代码跑得才快"——一个开源项目最真实的注脚,也是所有实验性技术起步时都会面对的窘境。

三、数字不会说谎:90MB 缩到 100KB

如果说原理听起来有点绕,数据是最直观的证明。

对比维度 传统(JS + 运行时) Porffor(AOT 编译)
原生二进制体积 ≈ 90MB < 100KB
WebAssembly 产物 打包解释器,体积大 真编译,比同类小 10–30 倍
运行方式 运行期解释/JIT 编译期生成,运行期零解释
冷启动 需唤醒运行时 直接执行机器码

一个简单的 hello world,用它编译后的原生二进制只有 几十 KB(官方演示里 hello world 原生二进制约 33.7KB)。这对边缘计算、Serverless、嵌入式、CLI 工具分发,是结构性的改变。

更妙的是它原生支持 TypeScript,不需要打包器、不需要额外的构建步骤:给它一个 .ts 文件,直接编。

img-compile

img-size

四、它真正撬动的,是四个场景

1. Serverless / 边缘计算。 作者本人就写文章介绍过:用 Porffor 编译的 JS 二进制,能在毫秒级启动,直接消除 AWS Lambda 的冷启动问题。边缘节点上,资源省下来的就是真金白银。

2. 小型 CLI 工具。 过去写命令行工具,很多 JS 开发者羡慕 Go/Rust 的"单文件发布"。Porffor 让 JS 开发者也有了这种体验:写 JS → 编译 → 一个几百 KB 的可执行文件。

3. 嵌入式 / 受限设备。 官方承诺"能用 C 的地方就能用 JS"——这是把 JS 的生态,送进以前装不下它的设备。

4. 安全与混淆。 编译成 WebAssembly 后是沙箱化的;而把 JS 直接编成二进制,比代码混淆更难反编译——对敏感代码是一种天然保护。

img-scenarios

五、必须诚实:它不是神话,仍在"实验期"

但我必须泼一盆冷水。Porffor 现在是研究性项目,它不是 Node 的生产替代品,至少现在不是。它的限制相当直白:

  • 异步支持不完整Promiseawait 还有已知 bug;
  • 不支持 eval / Function():AOT 编译下,动态求值天然做不了;
  • 跨作用域变量受限:很多 API 尚未实现。

它的版本号里甚至藏着这个"诚实":比如 0.61.10,意味着当前通过了 Test262(JS 规范测试套件)的 61%。也就是说,它目前支持的是 JS 的一个"子集",仍在持续逼近完整的 JavaScript。

从工程角度看,它本身是一个漂亮的作品:从零手写(除了复用 Acorn 解析器),自举(用它自己的代码编译自己),还自带一个 2c——把 Wasm 转回 C 的编译器。内存安全(用 JS 写)、完全 AOT、零 eval,这些叠加起来,是它安全性的底气。

img-limit

六、独立分析:编程语言的"经典三部曲",JS 正在补课

如果把视角拉高,你会发现这是语言发展的必然轨迹:从解释,到 JIT,再到 AOT。 C、C++、Rust 早就走完了这条路,而 JavaScript 因为"浏览器必须即编即用"的历史包袱,一直卡在解释 + JIT。

Porffor 的意义,不在于它今天能替代谁,而在于它证明了一件事:JavaScript 也可以在编译期,把"运行时"这一步提前消化掉。 它把"解释的优雅"和"编译的轻量"这两条原本互斥的路线,第一次真正结合在了一起。

未来的 JavaScript,可能不再只有"运行在浏览器里、跑在服务器上"两种形态,而是会出现在芯片、边缘设备、嵌入式环境——任何能跑 C 的地方。

当然,这条路并不会一帆风顺。JIT 之所以在浏览器里流行几十年,是因为它"即编即用、上手即跑",几乎零启动成本;而 AOT 的代价是把编译时间挪到了构建期。对于服务端这种"代码确定、环境可控"的场景,这笔账划算;但对浏览器里高度动态、需要即时响应的代码,AOT 未必是更好的答案。工具没有高下之分,只有适配的场景不同。

img-trend


也许再过几年回头看,Porffor 会像很多实验项目一样被遗忘。但它留下的那个问题,会一直在:

我们习以为常的"运行时",到底是 JavaScript 的宿命,还只是它还没走完的一站?

posted @ 2026-08-19 01:08  corysoft  阅读(2)  评论(0)    收藏  举报