本地安装 QuickJS 与 入门示例

简介

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 安装:

1
brew install cmake

或前往官网下载适配平台的二进制包:

安装 QuickJS

从 GitHub 获取 QuickJS 源码:

1
git clone https://github.com/bellard/quickjs

进入项目目录,使用 make 进行编译安装:

1
2
cd quickjs
sudo make install /usr/local

注意:
如果你本地 CMake 版本较新,可能在编译过程中会看到一些警告信息,直接忽略即可。

本地安装 QuickJS 与 入门示例

命令行工具

QuickJS 提供两个命令行工具:

  • qjs: JavaScript 解释器,可用于交互或执行 .js 文件;
  • qjsc: 编译器,将 .js 编译为原生可执行文件(无需解释器支持)。

交互式命令行

构建完成后,直接执行:

1
qjs

进入一个交互式 JavaScript Shell,语法高亮且可即时执行 JS 表达式。

本地安装 QuickJS 与 入门示例

命令行选项:
选项/参数 中文解释
-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)

编写一个简单脚本:

1
2
// hello.js
console.log('Hello QuickJS');

将其编译成可执行程序:

1
qjsc -o hello hello.js

运行:

1
2
./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,用于查找文件中包含特定文本的行。

示例源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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}`);
}

编译代码:

1
qjsc -o file-search file-search.js

运行脚本:

1
./file-search file-search.js const

示例结果:

本地安装 QuickJS 与 入门示例

解析说明

scriptArgs

这是 QuickJS 提供的全局变量,类似 Node.js 的 process.argv

1
['脚本路径', '参数1', '参数2', ...]

在脚本中可以通过 scriptArgs[1] 获取用户传入的文件名。

os.stat

1
const [statInfo, err] = os.stat(filename);

该方法返回一个数组:

  • 第一个元素是文件信息对象(包含 modesize 等);
  • 第二个是错误码,为 0 表示无错误。

mode 判断文件类型

由于 QuickJS 没有提供类似 isFile() 的 API,我们需要手动通过位运算判断:

1
(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 对象,支持逐行读取、写入等操作:

1
2
const file = std.open(filename, 'r');
file.getline(); // 一次读取一行,返回 null 表示文件结尾

相比 readFile 这种一次性读入内存的方式,这种方式更适合处理大文件。

结语

本文介绍了如何本地通过源码编译安装 QuickJS,同时讲了如何使用 qjsqjsc 两种工具,并通过一个实际示例简单演示了 QuickJS 的标准库用法。

相关链接


本地安装 QuickJS 与 入门示例
https://blog.pangcy.cn/2025/08/03/前端编程相关/JS Engine/QuickJS/本地安装 QuickJS 与 入门示例/
作者
子洋
发布于
2025年8月3日
许可协议