QuickJS介绍

QuickJS 是一个轻量级的 JavaScript 引擎,由 Fabrice Bellard 与 Charlie Gordon 开发。它符合 ECMAScript 标准,支持 ES2023 语法,体积小、嵌入方便,适用于嵌入式系统、游戏引擎、配置脚本等场景。

第 1 章:概述与背景

QuickJS 是一个由 Fabrice Bellard(著名的 FFmpeg、QEMU、TinyCC 作者)与 Charlie Gordon 共同开发的轻量级 JavaScript 引擎。
它以 高标准兼容性、极小体积、易于嵌入 为核心目标,成为现代嵌入式系统、游戏引擎、配置脚本系统与工具型软件的理想脚本引擎。

QuickJS 的诞生源于一个明确的需求:

“让开发者在资源受限的设备上,也能运行现代 ECMAScript(ES2023)语法,而无需庞大的 JIT 引擎。”

1.1 基本信息

项目内容
名称QuickJS
作者Fabrice Bellard, Charlie Gordon
许可证MIT License
官方网站https://bellard.org/quickjs/
当前版本(截至 2025)2024-01 版(社区有 QuickJS-NG 等分支)
语言C
架构类型解释执行(无 JIT)
平台支持Linux, macOS, Windows, BSD, 嵌入式 ARM/MIPS/RISC-V 等

1.2 QuickJS 的设计目标

  1. 符合 ECMAScript 标准

    • 支持 ES2023 的几乎所有特性(模块、Promise、async、Proxy、Map、WeakRef 等)。
  2. 极小的二进制体积

    • 核心库编译后仅数百 KB,可直接静态链接。
  3. 可嵌入与可移植

    • 可在 C/C++ 项目中作为脚本运行时,类似 Lua、AngelScript。
  4. 安全与确定性执行

    • 不依赖外部线程或系统调用,可在沙箱中执行用户脚本。
  5. 可扩展与可编译分发

    • 提供 qjsc 将 JS 源码编译为字节码或独立可执行文件。

第 2 章:设计理念与架构剖析

QuickJS 的架构基于“最小可实现 ECMAScript”理念。它不是简化版 JS,而是完整支持语言规范的轻量实现。

2.1 体系结构概览

┌──────────────────────────┐
│     JavaScript 源代码     │
└──────────────┬───────────┘
               │ Parser / Compiler
┌──────────────▼──────────────┐
│     字节码(Bytecode)       │
└──────────────┬──────────────┘
               │ Interpreter
┌──────────────▼──────────────┐
│  执行引擎(VM Core)         │
│  ├─ 堆管理 (GC)             │
│  ├─ 作用域与闭包模型        │
│  ├─ 对象与原型系统          │
│  ├─ Promise/Event Loop      │
└──────────────┬──────────────┘
               │
┌──────────────▼──────────────┐
│   Native API / C Bindings   │
└─────────────────────────────┘
flowchart TB
    A["JavaScript 源代码"] -->|Parser / Compiler| B["字节码 (Bytecode)"]
    B -->|Interpreter| C["执行引擎 (VM Core)"]
    C --> D["Native API / C Bindings"]

    %% VM Core 内部子模块
    subgraph C ["执行引擎 (VM Core)"]
        C1["堆管理 (GC)"]
        C2["作用域与闭包模型"]
        C3["对象与原型系统"]
        C4["Promise / Event Loop"]
    end

    C1 --> C2
    C2 --> C3
    C3 --> C4

2.2 内部模块划分

模块功能描述
quickjs.c核心 VM 实现(编译器、解释器、GC、标准类型)
libbf.cBigFloat / BigDecimal 高精度运算库
libregexp.c正则表达式引擎
libunicode.cUnicode 支持
cutils.c通用工具函数(字符串处理、内存操作)
quickjs-libc.c提供标准库封装(std, os 模块)

第 3 章:ECMAScript 标准支持详解

QuickJS 支持 ECMAScript 2023 的大部分特性:

特性支持情况备注
let / const完全支持块级作用域
class / extends支持原型继承与静态字段
async / await支持 Promise 与异步生成器
Proxy / Reflect实现完整代理 API
Map / Set / WeakMap / WeakSet完整实现
BigInt原生支持
BigFloat / BigDecimalQuickJS 扩展特性
模块(import/export)支持本地与内置模块
Intl API⚠️部分实现(无完整本地化)
Web API无 DOM/Fetch(需自行扩展)

QuickJS 是少数无 JIT 但仍完整支持 ES 模块的引擎之一。

第 4 章:核心运行时与内存模型

4.1 Runtime / Context 概念

QuickJS 的执行模型基于两层结构:

  • JSRuntime:运行时对象,包含 GC、模块缓存、全局配置。
  • JSContext:执行上下文,保存变量作用域、对象、异常状态等。

一个运行时可拥有多个上下文,用于隔离脚本环境。

4.2 垃圾回收机制

QuickJS 使用 标记-清除(Mark-Sweep)GC,并提供手动触发接口:

JS_RunGC(rt); // 手动执行一次 GC

它支持弱引用与 Finalizer,可安全管理 C 资源绑定。

4.3 类型系统与 JSValue

JSValue 是所有 JavaScript 值的统一封装类型。

typedef union JSValueUnion {
    int32_t int32;
    double float64;
    void *ptr;
#if JS_SHORT_BIG_INT_BITS == 32
    int32_t short_big_int;
#else
    int64_t short_big_int;
#endif
} JSValueUnion;

typedef struct JSValue {
    JSValueUnion u;
    int64_t tag;
} JSValue;

通过 JS_TAG_* 区分类型(如 JS_TAG_STRING, JS_TAG_OBJECT 等)。
开发者通过 JS_ToInt32, JS_ToCString, JS_NewInt32 等 API 进行类型转换。

第 5 章:嵌入式 API 与原生扩展接口

QuickJS 通过 quickjs.h 提供完整 C API,可实现:

  • 执行字符串脚本;
  • 注册原生函数;
  • 绑定 C 结构到 JS 对象;
  • 加载 JS 模块;
  • 自定义内存分配与错误处理。

5.1 执行脚本示例

JSValue val = JS_Eval(ctx, "1 + 2", strlen("1 + 2"),
                      "<eval>", JS_EVAL_TYPE_GLOBAL);

5.2 注册原生函数

static JSValue js_log(JSContext *ctx, JSValueConst this_val,
                      int argc, JSValueConst *argv) {
    const char *str = JS_ToCString(ctx, argv[0]);
    printf("[LOG] %s\n", str);
    JS_FreeCString(ctx, str);
    return JS_UNDEFINED;
}

JS_NewCFunction(ctx, js_log, "log", 1);
JS_SetPropertyStr(ctx, global_obj, "log",
                  JS_NewCFunction(ctx, js_log, "log", 1));

JS 侧调用:

log("Hello from C!");

第 6 章:命令行工具 qjs 与 qjsc

6.1 qjs 解释器

qjs 类似 Node.js CLI,可直接运行 JS 文件:

$ qjs script.js

内置模块:

  • std:文件、控制台、进程 API;
  • os:系统调用与路径操作。

6.2 qjsc 编译器

将 JS 编译为字节码或可执行程序:

$ qjsc -o hello hello.js
$ ./hello

也可输出 C 源文件:

$ qjsc -o hello.c hello.js
$ gcc hello.c -lquickjs -o hello

支持选项:

  • -c:生成 .c 文件;
  • -m:启用模块模式;
  • -fno-std:不包含 std 模块;
  • -e:编译为字节码并执行。

第 7 章:模块系统与加载机制

QuickJS 模块完全遵循 ECMAScript Module 规范。

// math.js
export function add(a, b) { return a + b; }

// main.js
import { add } from './math.js';
console.log(add(1, 2));

qjs 执行时根据当前目录解析模块路径。
嵌入式环境可通过 JS_SetModuleLoaderFunc 自定义模块加载逻辑(如从内存或数据库中加载)。

第 8 章:Big 系列类型与 math 模式

QuickJS 独有的数值扩展包括:

  • BigInt:大整数(标准)
  • BigFloat:任意精度浮点
  • BigDecimal:任意精度十进制
  • operator overloading:可自定义 +, -, *, / 等操作符行为。
"use math";
let x = 1.2345bf;  // BigFloat
let y = 2.0bf;
print(x * y);

通过 "use math" 可启用高精度模式。

第 9 章:与其他引擎对比

特性QuickJSV8JavaScriptCoreDuktapeJerryScript
体积~500KB>25MB>10MB~600KB~200KB
JIT
模块支持⚠️⚠️
ES2023 兼容
BigFloat
嵌入易用性
可移植性

QuickJS 在标准支持度 + 体积 + 可嵌入性之间取得最佳平衡。

第 10 章:应用场景与案例实践

  1. 游戏脚本系统(如 Defold、Skynet 项目)

    • 将 QuickJS 作为规则引擎;
    • 玩家行为逻辑通过 JS 编写并热加载;
    • 服务器端可限制 CPU/内存以防滥用。
  2. 可配置规则系统

    • 业务规则由 JS 脚本配置;
    • 动态执行时无需重新编译 C 程序。
  3. 轻量 CLI 工具

    • 通过 qjsc 将 JS 编译为单文件执行程序;
    • 类似“无 Node 的脚本工具”。
  4. 嵌入式设备

    • IoT、智能家居中用于运行脚本逻辑;
    • 支持 ARM/MIPS/RISC-V。

第 11 章:性能优化与工程集成策略

11.1 优化策略

  • 使用 预编译字节码 提升加载速度;
  • 合理使用 JS_RunGC() 控制内存;
  • 在多上下文场景复用 Runtime;
  • 避免频繁 C ⇄ JS 切换。

11.2 与其他语言集成

  • C/C++:直接链接;
  • Go:通过 CGO 封装(如 quickjs-go);
  • Rust:通过 quickjs-rs
  • Python:通过 ctypes 或 pybind 调用。

第 12 章:生态、社区与未来方向

12.1 QuickJS-NG

社区维护分支,提供:

  • 更完善的测试;
  • Windows/macOS 打包;
  • WebAssembly 目标。

12.2 WebAssembly 计划

QuickJS 已能编译为 Wasm 运行于浏览器或沙盒中,
可实现“JS 解释 JS”实验性环境。

12.3 未来方向

  • 更好的多线程支持;
  • 可选的 JIT(外部模块);
  • 内置 TypeScript 预编译;
  • 与 Rust/Wasm 的深度融合。

结语

QuickJS 并不是要取代 V8,而是填补 “轻量标准兼容 JS 引擎” 这一空白。
它让开发者能在极低资源成本下运行现代 JavaScript,在工具、游戏、嵌入式与边缘计算领域中拥有广阔前景。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页