QuickJS C API完全指南:如何将JavaScript引擎嵌入到你的应用中
QuickJS是一个小型并且可嵌入的JavaScript引擎,它支持ES2020规范,包括模块、异步生成器和代理器。这个轻量级JavaScript运行时以其小巧的体积和出色的性能而闻名,是嵌入式系统和资源受限环境的理想选择。本终极指南将带你深入了解QuickJS C API,掌握如何将JavaScript引擎无缝集成到你的C/C++应用程序中。## 🚀 为什么选择QuickJS嵌入式Java
QuickJS C API完全指南:如何将JavaScript引擎嵌入到你的应用中
QuickJS是一个小型并且可嵌入的JavaScript引擎,它支持ES2020规范,包括模块、异步生成器和代理器。这个轻量级JavaScript运行时以其小巧的体积和出色的性能而闻名,是嵌入式系统和资源受限环境的理想选择。本终极指南将带你深入了解QuickJS C API,掌握如何将JavaScript引擎无缝集成到你的C/C++应用程序中。
🚀 为什么选择QuickJS嵌入式JavaScript引擎?
QuickJS嵌入式JavaScript引擎具有多项独特优势,使其成为嵌入式开发的理想选择:
- 极致小巧:完整引擎仅需少量C文件,无外部依赖,在x86上"hello world"程序仅约210 KiB
- 启动迅速:运行时实例的完整生命周期在不到300微秒内完成
- 标准兼容:几乎完整的ES2023支持,包括模块、异步生成器和完整的Annex B
- 内存高效:采用引用计数结合循环删除的垃圾回收机制
- 易于嵌入:简洁的C API设计,学习曲线平缓
📦 快速入门:编译与安装
首先克隆QuickJS仓库并编译:
git clone https://gitcode.com/gh_mirrors/qui/QuickJS
cd QuickJS
make
make install
编译成功后,你将获得两个主要工具:
- qjs:命令行解释器(REPL)
- qjsc:JavaScript到可执行文件的编译器
尝试运行示例程序:
./qjs examples/hello.js
🔧 QuickJS C API核心概念
运行时与上下文
QuickJS的核心架构基于两个基本概念:
- JSRuntime:表示JavaScript运行时,对应对象堆
- JSContext:表示JavaScript上下文(Realm),每个上下文有自己的全局对象
// 创建运行时和上下文
JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
JSValue类型系统
JSValue是QuickJS中表示JavaScript值的核心结构:
// 创建JavaScript值
JSValue val = JS_NewString(ctx, "Hello, QuickJS!");
JSValue num = JS_NewInt32(ctx, 42);
JSValue obj = JS_NewObject(ctx);
// 必须正确管理引用计数
JS_DupValue(ctx, val); // 增加引用计数
JS_FreeValue(ctx, val); // 减少引用计数
异常处理机制
QuickJS使用显式的异常处理模式:
JSValue result = JS_Eval(ctx, script, strlen(script), "<eval>", 0);
if (JS_IsException(result)) {
JSValue exception = JS_GetException(ctx);
const char *str = JS_ToCString(ctx, exception);
printf("Exception: %s\n", str);
JS_FreeCString(ctx, str);
JS_FreeValue(ctx, exception);
}
JS_FreeValue(ctx, result);
🛠️ 实用C API函数指南
执行JavaScript代码
使用JS_Eval()函数执行JavaScript脚本:
// 执行简单脚本
const char *script = "1 + 2 * 3";
JSValue result = JS_Eval(ctx, script, strlen(script), "<eval>", 0);
int32_t num;
JS_ToInt32(ctx, &num, result);
printf("Result: %d\n", num); // 输出: 7
JS_FreeValue(ctx, result);
注册C函数到JavaScript
将C函数暴露给JavaScript环境:
// C函数实现
JSValue js_print(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
for (int i = 0; i < argc; i++) {
const char *str = JS_ToCString(ctx, argv[i]);
if (str) {
printf("%s ", str);
JS_FreeCString(ctx, str);
}
}
printf("\n");
return JS_UNDEFINED;
}
// 注册到全局对象
JSValue global_obj = JS_GetGlobalObject(ctx);
JS_SetPropertyStr(ctx, global_obj, "print",
JS_NewCFunction(ctx, js_print, "print", 1));
JS_FreeValue(ctx, global_obj);
创建自定义JavaScript类
定义具有C数据绑定的JavaScript类:
// 定义类ID和类
static JSClassID my_class_id;
static void my_class_finalizer(JSRuntime *rt, JSValue val) {
MyData *data = JS_GetOpaque(val, my_class_id);
if (data) {
free(data);
}
}
// 注册类
JS_NewClassID(&my_class_id);
JSClassDef class_def = {
"MyClass",
.finalizer = my_class_finalizer,
};
JS_NewClass(rt, my_class_id, &class_def);
// 创建类实例
JSValue obj = JS_NewObjectClass(ctx, my_class_id);
MyData *data = malloc(sizeof(MyData));
JS_SetOpaque(obj, data);
📁 模块系统集成
加载JavaScript模块
QuickJS完全支持ES6模块系统:
// 加载模块文件
const char *module_code = "export function add(a, b) { return a + b; }";
JSValue module = JS_Eval(ctx, module_code, strlen(module_code),
"module.mjs", JS_EVAL_TYPE_MODULE);
// 获取模块导出
JSValue exports = JS_GetModuleExport(ctx, module);
JSValue add_func = JS_GetPropertyStr(ctx, exports, "add");
创建原生C模块
实现动态加载的原生模块:
// 模块初始化函数
JSModuleDef *js_my_module_init(JSContext *ctx, const char *module_name) {
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_my_module_init);
// 添加导出函数
JS_AddModuleExport(ctx, m, "myFunction");
return m;
}
// 模块函数实现
JSValue js_my_function(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
return JS_NewString(ctx, "Hello from C module!");
}
🔍 内存管理与优化
内存限制设置
// 设置内存限制(字节)
JS_SetMemoryLimit(rt, 64 * 1024 * 1024); // 64MB
// 设置栈大小
JS_SetMaxStackSize(rt, 1024 * 1024); // 1MB
自定义内存分配器
static void *my_malloc(JSMallocState *s, size_t size) {
return malloc(size);
}
static void my_free(JSMallocState *s, void *ptr) {
free(ptr);
}
// 使用自定义分配器创建运行时
JSMallocFunctions mf = {
.js_malloc = my_malloc,
.js_free = my_free,
};
JSRuntime *rt = JS_NewRuntime2(&mf, NULL);
🎯 高级特性与技巧
执行超时控制
static int interrupt_handler(JSRuntime *rt, void *opaque) {
int64_t *timeout = opaque;
if (--(*timeout) <= 0) {
return 1; // 中断执行
}
return 0; // 继续执行
}
// 设置中断处理器
int64_t timeout = 1000; // 1000次检查后超时
JS_SetInterruptHandler(rt, interrupt_handler, &timeout);
字节码编译与执行
// 编译JavaScript为字节码
uint8_t *out_buf;
size_t out_len;
JS_Eval(ctx, script, strlen(script), "<input>",
JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_GLOBAL);
// 执行预编译的字节码
JSValue result = js_std_eval_binary(ctx, out_buf, out_len, 0);
📊 性能调优建议
- 重用运行时和上下文:避免频繁创建销毁
- 合理设置内存限制:根据应用需求调整
- 预编译常用脚本:使用
qjsc编译为字节码 - 禁用未使用特性:通过
-fno-*选项减小体积 - 使用引用计数最佳实践:及时释放不再使用的JSValue
🧪 测试与调试
QuickJS包含完整的测试套件:
# 运行内置测试
make test
# 运行ECMAScript测试套件
make test2
查看测试文件了解API用法:
- tests/test_builtin.js - 内置函数测试
- tests/test_closure.js - 闭包测试
- tests/test_worker.js - 工作线程测试
📚 进一步学习资源
- 官方C API文档:quickjs.h
- 标准库实现:quickjs-libc.c
- 示例代码:examples/
- 编译器实现:qjsc.c
🎉 开始你的嵌入之旅
QuickJS C API提供了强大而简洁的接口,让你能够轻松地将JavaScript运行时嵌入到任何C/C++应用程序中。无论你是构建嵌入式设备、游戏脚本系统,还是需要轻量级JavaScript运行时的桌面应用,QuickJS都能满足你的需求。
记住,成功嵌入的关键在于:
- 正确管理JSValue的生命周期
- 合理设置内存和资源限制
- 充分利用模块系统
- 遵循异常处理最佳实践
现在就开始探索QuickJS的强大功能,为你的应用添加灵活的脚本能力吧!🚀
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)