QuickJS介绍
目录
第 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 的设计目标
-
符合 ECMAScript 标准
- 支持 ES2023 的几乎所有特性(模块、Promise、async、Proxy、Map、WeakRef 等)。
-
极小的二进制体积
- 核心库编译后仅数百 KB,可直接静态链接。
-
可嵌入与可移植
- 可在 C/C++ 项目中作为脚本运行时,类似 Lua、AngelScript。
-
安全与确定性执行
- 不依赖外部线程或系统调用,可在沙箱中执行用户脚本。
-
可扩展与可编译分发
- 提供 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.c | BigFloat / BigDecimal 高精度运算库 |
| libregexp.c | 正则表达式引擎 |
| libunicode.c | Unicode 支持 |
| 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 / BigDecimal | ✅ | QuickJS 扩展特性 |
| 模块(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 JSValue {
int64_t tag;
void *ptr;
} 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 章:与其他引擎对比
| 特性 | QuickJS | V8 | JavaScriptCore | Duktape | JerryScript |
|---|---|---|---|---|---|
| 体积 | ~500KB | >25MB | >10MB | ~600KB | ~200KB |
| JIT | ❌ | ✅ | ✅ | ❌ | ❌ |
| 模块支持 | ✅ | ✅ | ✅ | ⚠️ | ⚠️ |
| ES2023 兼容 | ✅ | ✅ | ✅ | ❌ | ❌ |
| BigFloat | ✅ | ❌ | ❌ | ❌ | ❌ |
| 嵌入易用性 | ✅ | ❌ | ❌ | ✅ | ✅ |
| 可移植性 | 高 | 中 | 中 | 高 | 高 |
QuickJS 在标准支持度 + 体积 + 可嵌入性之间取得最佳平衡。
第 10 章:应用场景与案例实践
-
游戏脚本系统(如 Defold、Skynet 项目)
- 将 QuickJS 作为规则引擎;
- 玩家行为逻辑通过 JS 编写并热加载;
- 服务器端可限制 CPU/内存以防滥用。
-
可配置规则系统
- 业务规则由 JS 脚本配置;
- 动态执行时无需重新编译 C 程序。
-
轻量 CLI 工具
- 通过 qjsc 将 JS 编译为单文件执行程序;
- 类似“无 Node 的脚本工具”。
-
嵌入式设备
- 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, 在工具、游戏、嵌入式与边缘计算领域中拥有广阔前景。