跪拜 Guibai
← All articles
Frontend · Backend · Developer

moejs: Pure Go JavaScript Runtime Beats C QuickJS by 35%

By 前端之虎陈随易 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Go developers building plugin systems or AI gateways can now get per-request JavaScript isolation without cgo, at speeds that beat C QuickJS on large payloads. The trade-off is that moejs is alpha, has incomplete spec coverage, and its bare runtime needs host-supplied features like event loop and fetch.

Summary

moejs 0.1.0, released November 9, 2026 by Calcium-Ion (maintainer of the AI gateway new-api), is a JavaScript runtime written entirely in Go. Its niche: letting Go programs run many JavaScript plugins, each request isolated in its own sandbox. The repo, created September 24, already has 532 stars. It sidesteps the usual trade-off between pure Go engines (easy cross-compilation, middling performance) and cgo engines (fast but requiring a C toolchain). moejs claims both: pure Go and speed.

On the V8 benchmark suite v7, moejs scores 1429 against 1058 for C QuickJS, winning all eight mini-programs. Its own prior version dropped from 41.55s to 14.29s on the same tests. The plugin scenario is where the numbers matter most: with new-api's ten task plugins, a 100k-token agent request under 8 concurrency handles 2.9x more requests per second than C QuickJS; an 8 MiB image request runs 8.6x faster. Small requests are 7% slower. Runtime creation costs 1.4 µs (Sobek 2.2, quickjs-go 382), and with the largest plugin a runtime uses 81 KiB (Sobek 264, quickjs-go 348).

Sandbox isolation is central: plugins only see the standard library plus host-installed globals, with every import gated through Go functions; eval and new Function can be limited or disabled. Built-ins are frozen and runtimes share a single Array.prototype, preventing cross-plugin mutation. node:fs runs inside an os.Root directory jail. JSON parsing avoids copying, pointing directly into request memory. test262 passes 80,971 cases with zero failures, but 14,268 are skipped for unimplemented features like Temporal and decorators. TypeScript runs via CompileTS, which only strips types. Still alpha, the API will change. The sole dependency is moego for SIMD base64, and it builds with CGO_ENABLED=0.

Takeaways
— moejs 0.1.0 shipped November 9, 2026, from a repo created September 24, 2026.
— The GitHub repo had 532 stars at the time of writing.
— On V8 benchmark suite v7, moejs scores 1429 vs 1058 for C QuickJS (1.35x).
— moejs wins all eight V8 benchmark mini-programs.
— Its own previous version ran the same tests in 41.55s; now it takes 14.29s.
— Plugin benchmark: 2.9x higher throughput on 100k-token agent requests at 8 concurrency.
— Requests with 8 MiB images process 8.6x faster than C QuickJS; small requests are 7% slower.
— A single plugin call takes 6.9 µs; Sobek takes 14.3 µs, quickjs-go 104.5 µs.
— Creating a runtime takes 1.4 µs; Sobek 2.2 µs, quickjs-go 382 µs.
— With largest plugin, runtime memory is 81 KiB; Sobek 264, quickjs-go 348.
— test262: 80,971 cases passed with zero failures; 14,268 skipped for missing features.
— Only dependency is moego for SIMD base64; builds with CGO_ENABLED=0.
— Bare runtime lacks setTimeout and fetch; requires eventloop package or host http.Client.
— Sandbox uses host-gated imports, frozen built-ins, shared Array.prototype, and os.Root filesystem jail.
— ParseJSONString avoids copying, pointing strings into the request's memory block.
Conclusions

The benchmark story is scoped to exactly where moejs shines: runtime creation and JSON parsing dominate AI-gateway plugin costs, and those are the areas where the engine deliberately optimized. Small requests still go to C QuickJS, so the headline speedup doesn't apply universally.

Skipping 14,268 test262 cases means the speed claims hold for a subset of JavaScript, not the full spec; features like Temporal and decorators are simply absent.

A frozen, shared Array.prototype across runtimes is a neat isolation trick that eliminates per-runtime object copies, but it depends on host discipline for gating globals.

Being pure Go with no cgo gives moejs a real deployment edge: any Go target that supports CGO_ENABLED=0 can embed it, which historically has been the blocker for shipping cgo-based engines.

Concepts & terms
test262
The official ECMAScript conformance test suite that runs thousands of cases to verify a JavaScript engine's spec compliance.
os.Root
A directory-bound jail in Go's os package that restricts all file operations to a given root directory, blocking `..` and symlink escapes.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗