moejs,纯Go实现的JS引擎,V8Benchmark跑分达1429分,超C版QuickJS35%

由 Calcium-Ion 开发的 moejs 是一个纯 Go 编写的 JavaScript 运行时,2026 年 11 月发布 0.1.0 版本。在 V8 Benchmark Suite v7 测试...

互联网/IT 阅读量: 1.68万

一个用 Go 语言从零实现的 JavaScript 引擎,在发布不到两个月内获得 500+ GitHub Star,并在性能测试中显著超越长期被视作轻量级标杆的 C 语言 QuickJS——这并非虚构的开源奇迹,而是真实发生在 2026 年底的开发者实践。

moejs 项目于 2026 年 9 月 24 日创建,11 月 9 日正式发布 0.1.0 版本,作者为独立开发者 Calcium-Ion,同时也是 API 网关项目 new-api 的维护者。其设计初衷非常明确:为 Go 编写的 AI 网关提供高效、隔离、可复用的 JavaScript 插件沙箱支持。在 new-api 的典型工作流中,一次请求需经过多个 JS 钩子处理,每个钩子都应运行在独立、无副作用的运行时环境中。

当前主流的 Go 中嵌入 JS 方案大致分为两类:一类是纯 Go 实现(如 goja、Sobek),优势在于无需 CGO、易于交叉编译,但性能普遍受限;另一类依赖 C 库封装(如 quickjs-go、v8go),性能更强,却牺牲了构建简洁性与平台兼容性。moejs 的核心目标,正是弥合这一鸿沟——在保持纯 Go 架构的前提下,实现接近甚至超越 C 层引擎的执行效率。

性能数据是其最有力的证明。在 V8 Benchmark Suite v7 的八个经典小程序综合测试中,moejs 得分 1429,而同机测试的 C 版 QuickJS 为 1058 分,前者为后者的 1.35 倍。更值得注意的是,八个子测试项中,moejs 在每一项上均领先。相比其自身 0.1.0-alpha.11 版本(耗时 41.55 秒),新版本将总测试时间压缩至 14.29 秒,提速近 3 倍。

但在实际业务场景中,单纯看“跑分”并不足以反映价值。new-api 的插件负载测试更能体现 moejs 的工程优势:针对 10 个任务插件组成的链路,处理一个含 10 万 token 的 Agent 请求时,在 8 并发下,moejs 每秒请求数为 C 版 QuickJS 的 2.9 倍;当请求体包含 8 MiB 图片时,该倍数跃升至 8.6 倍。

这种差异源于 moejs 对“运行时生命周期成本”的极致优化。在插件系统中,高频操作是“新建运行时→加载模块→调用函数→回收”,而非长时间持续执行单一脚本。moejs 将单次插件调用延迟压至 6.9 微秒,远低于 Sobek 的 14.3 微秒与 quickjs-go 的 104.5 微秒;新建一个运行时仅需 1.4 微秒,而 quickjs-go 需 382 微秒——相差两个数量级。

内存开销同样关键。装入最大插件后,一个 moejs 运行时仅占 81 KiB,Sobek 为 264 KiB,quickjs-go 达 348 KiB。这意味着在高并发插件场景下,moejs 可支撑数倍于竞品的实例密度。

技术实现上,moejs 采用编译一次、运行时复用的模式:先通过 moejs.Compile 将源码编译为模块,再从对象池中获取 Runtime 实例,设置全局变量(如 utils.env),加载模块,调用函数并解析结果。其中 ParseJSONString 不复制输入字符串,直接复用原始内存地址,大幅减少大请求下的拷贝开销。

V8 benchmark suite v7,八个程序的总分

安全与隔离机制亦经精心设计。所有内置对象(如 Array.prototype)被冻结且跨运行时共享,防止插件间污染;import 和动态 import() 均需经宿主显式授权;eval 与 new Function 可配置禁用或长度限制;文件系统访问被限定在宿主指定的根目录内,软链接与路径穿越均被阻断。

兼容性方面,moejs 已通过 test262 测试套件中的 80,971 个用例,零失败;另有 14,268 个用例因未实现 Temporal、装饰器、Iterator Helpers 或部分 Intl API 而跳过。TypeScript 支持通过 CompileTS 实现,仅剥离类型注解,保留语义与错误定位能力,堆栈信息仍指向原始 TS 源码。

目前项目处于 Alpha 阶段,API 尚不稳定。基础运行时不包含 setTimeout 或 fetch,需分别引入 eventloop 包与宿主提供的 http.Client 实例。依赖仅有一个 moego(用于 base64 的 SIMD 加速),引擎主体完全不依赖 CGO,支持 CGO_ENABLED=0 编译。

一次插件调用要多久,一个运行时占多少内存

从行业视角看,moejs 的出现反映了后端开发对“轻量级、高并发、强隔离”脚本能力的迫切需求。随着 AI 网关、边缘计算、规则引擎等场景普及,传统嵌入式 JS 引擎的构建复杂性与资源消耗日益成为瓶颈。Go 作为云原生时代的核心语言,若能原生高效承载 JS 插件逻辑,将极大简化系统架构。

文章配图

对比同类方案:goja 以易用性见长但性能不足;Sobek 试图平衡速度与纯 Go,仍落后于 moejs;modernc.org/quickjs 虽为纯 Go 翻译版,但内存占用更高;而 quickjs-go 和 v8go 在性能上虽有优势,却难以满足容器化、多平台部署等现代 DevOps 要求。

moejs 并非要取代 V8 或 QuickJS,而是精准切入“微服务级 JS 插件沙箱”这一细分战场。它证明:在特定约束条件下,纯高级语言实现不仅能达成工程实用性,甚至可在关键指标上反超底层语言方案。

对于正在构建插件化系统的 Go 开发者而言,moejs 提供了一个值得评估的新选项——尤其当交叉编译便利性、内存效率与启动延迟比绝对峰值性能更重要时。项目仓库已开源:https://github.com/MoeForge/moejs。