简介
QuickJS 是一个小巧、易嵌入的 JavaScript 引擎,由 Fabrice Bellard 大神编写。它的目标是高度兼容 ECMAScript 标准,并具备快速启动、低内存占用的特性,适用于嵌入式环境或需要快速初始化脚本执行的场景。
它的主要特点包括:
- 极小体积,便于嵌入:整个引擎由几个 C 文件构成,无需依赖其他库,在 x86 上一个最小 "hello world" 程序仅约 210 KiB。
- 快速解释器,启动极快:可在台式机单核下,2 分钟内跑完 ECMAScript 测试套件的 77,000 个测试用例。
- 高度标准兼容:几乎完整支持 ES2023 标准,并部分实现 ES2024 新增特性。
- 无需外部依赖的独立可执行文件:可将 JS 源码直接编译成可执行文件。
- 引用计数 GC + 循环回收:使用引用计数进行垃圾回收,同时支持循环引用的清理。
- 命令行交互器带语法高亮,并可在 JS 中进行扩展。
- 内建标准库小巧实用:提供对 libc 和底层 OS 功能的封装。
安装
安装 CMake
QuickJS 使用 CMake
作为构建系统,所以需要先安装它。
推荐通过 Homebrew 安装:
bash
brew install cmake
或前往官网下载适配平台的二进制包:
安装 QuickJS
从 GitHub 获取 QuickJS 源码:
bash
git clone https://github.com/bellard/quickjs
进入项目目录,使用 make
进行编译安装:
bash
cd quickjs
sudo make install /usr/local
注意: 如果你本地 CMake 版本较新,可能在编译过程中会看到一些警告信息,直接忽略即可。

命令行工具
QuickJS 提供两个命令行工具:
qjs
: JavaScript 解释器,可用于交互或执行.js
文件;qjsc
: 编译器,将.js
编译为原生可执行文件(无需解释器支持)。
交互式命令行
构建完成后,直接执行:
bash
qjs
进入一个交互式 JavaScript Shell,语法高亮且可即时执行 JS 表达式。

命令行选项:
选项/参数 | 中文解释 |
---|---|
-h / --help |
显示帮助信息,列出所有支持的选项。 |
-e EXPR / --eval EXPR |
直接执行指定的 JavaScript 表达式(EXPR ),无需文件输入。 |
-i / --interactive |
进入交互模式(默认情况下,若提供文件参数则不会自动进入交互模式)。 |
-m / --module |
强制将文件作为 ES6 模块加载(默认根据扩展名或 import 关键字自动判断)。 |
--script |
强制将文件作为 ES6 脚本加载(默认自动判断)。 |
-I file / --include file |
在运行主文件前,先加载并执行指定的附加文件(file )。 |
--std |
即使脚本非模块模式,也强制启用 std 和 os 内置模块的支持。 |
-d / --dump |
输出内存使用统计信息(用于调试或性能分析)。 |
-q / --quit |
仅初始化解释器后立即退出(不执行任何脚本,用于测试或环境检查)。 |
编译器使用(qjsc)
编写一个简单脚本:
js
// hello.js
console.log('Hello QuickJS');
将其编译成可执行程序:
bash
qjsc -o hello hello.js
运行:
bash
./hello
# 输出:Hello QuickJS
命令行选项
选项/参数 | 中文解释 |
---|---|
-c |
仅生成包含字节码的 C 文件(默认输出可执行文件)。 |
-e |
生成包含 main() 函数和字节码的 C 文件(默认输出可执行文件)。 |
-o output |
指定输出文件名(默认为 out.c 或 a.out )。 |
-N cname |
设置生成数据(如字节码数组)的 C 变量名称。 |
-m |
强制按 ES6 模块编译(默认根据 .mjs 扩展名或 import 关键字自动判断)。 |
-D module_name |
编译动态加载模块及其依赖(需配合 import 或 os.Worker 使用)。 |
-M module_name[,cname] |
为外部 C 模块添加初始化代码(参考 c_module 示例)。 |
-x |
生成字节序交换的输出(仅用于交叉编译)。 |
-flto |
启用链接时优化(编译较慢但生成更小更快的可执行文件)。 |
-fno-[feature] |
禁用指定语言特性以生成更小的可执行文件(如 -fno-eval 禁用 eval 功能)。 |
示例:文件内容搜索
下面是一个使用 QuickJS 编写的文件搜索工具,功能类似 grep
,用于查找文件中包含特定文本的行。
示例源码
js
import * as os from 'os'
import * as std from 'std'
"use strict";
const colors = {
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
reset: '\x1b[0m'
}
function isFile(mode) {
return mode & os.S_IFMT === os.S_IFREG;
}
/**
* 简单的文件搜索函数
* @param {*} filename
* @param {*} searchText
* @returns
*/
function searchInFile(filename, searchText) {
try {
const [fileObj, err] = os.stat(filename);
if (isFile(fileObj?.mode) || err !== 0) {
console.log(`${colors.red}错误: ${filename} 不是文件${colors.reset}`)
return
}
const file = std.open(filename, 'r');
if (!file) throw new Error(`无法打开文件 ${filename}`);
let lineNumber = 1;
let found = false;
let i = 0;
while (true) {
const line = file.getline();
if (line === null) break;
if (line.includes(searchText)) {
found = true;
console.log(`${colors.green}${filename}:${lineNumber}${colors.reset} ${highlightText(line, searchText)}`);
}
lineNumber++;
}
file.close();
if (!found) {
console.log(`${colors.yellow}未找到 "${searchText}"${colors.reset}`);
}
} catch (error) {
console.log(`${colors.red}错误: ${error.message}${colors.reset}`);
}
}
/**
* 高亮匹配文本
* @param {*} line
* @param {*} searchText
* @returns
*/
function highlightText(line, searchText) {
return line.replace(
new RegExp(searchText, 'g'),
`${colors.yellow}${searchText}${colors.reset}`
)
}
/**
* scriptArgs 是一个全局对象,用于提供命令行参数,第一个参数是脚本名称。
* 类似 Node.js 中的 process.argv;
*/
if (typeof scriptArgs !== 'undefined' && scriptArgs.length >= 3) {
const filename = scriptArgs[1];
const searchText = scriptArgs[2];
searchInFile(filename, searchText);
} else {
console.log(`${colors.yellow}使用方法: ./file-search <文件名> <搜索文本>${colors.reset}`);
}
编译代码:
bash
qjsc -o file-search file-search.js
运行脚本:
arduino
./file-search file-search.js const
示例结果:

解析说明
scriptArgs
这是 QuickJS 提供的全局变量,类似 Node.js 的 process.argv
:
js
['脚本路径', '参数1', '参数2', ...]
在脚本中可以通过 scriptArgs[1]
获取用户传入的文件名。
os.stat
js
const [statInfo, err] = os.stat(filename);
该方法返回一个数组:
- 第一个元素是文件信息对象(包含
mode
、size
等); - 第二个是错误码,为 0 表示无错误。
mode
判断文件类型
由于 QuickJS 没有提供类似 isFile()
的 API,我们需要手动通过位运算判断:
js
(mode & os.S_IFMT) === os.S_IFREG
S_IFMT
:提取文件类型位的掩码;S_IFREG
:常量,表示"普通文件"。
QuickJS 中的 mode
属性常量,与 C/C++ 编程语言中定义在 POSIX(Unix/Linux 等类 Unix 系统)下的一个系统头文件 <sys/stat.h>
一致,主要用于 文件状态 的相关操作。
S_IFMT |
type of file | 文件类型掩码,用于提取 st_mode 中表示文件类型的位。 |
---|---|---|
S_IFBLK |
block special | 块设备文件,如磁盘分区,数据按固定大小的块读写。 |
S_IFCHR |
character special | 字符设备文件,如终端或键盘,数据以字符流形式传输。 |
S_IFIFO |
FIFO special | 命名管道(FIFO),用于进程间通信,数据按先进先出顺序处理。 |
S_IFREG |
regular | 普通文件,存储用户数据(如文本、二进制文件)。 |
S_IFDIR |
directory | 目录文件,包含其他文件的名称和索引节点(inode)指针。 |
S_IFLNK |
symbolic link | 符号链接(软链接),指向另一个文件的路径名而非 inode。 |
std.open
和逐行读取
QuickJS 的 std
模块封装了 fopen
,返回一个 FILE
对象,支持逐行读取、写入等操作:
js
const file = std.open(filename, 'r');
file.getline(); // 一次读取一行,返回 null 表示文件结尾
相比 readFile
这种一次性读入内存的方式,这种方式更适合处理大文件。
结语
本文介绍了如何本地通过源码编译安装 QuickJS,同时讲了如何使用 qjs
和 qjsc
两种工具,并通过一个实际示例简单演示了 QuickJS 的标准库用法。
相关链接
- QuickJS 仓库: github.com/bellard/qui...
- 官方文档: bellard.org/quickjs/qui...