process 是什么?

process 是 Node.js 中的一个全局对象,它提供了当前 Node.js 进程的相关信息,并允许你对其进行控制。

process基础教程
(图片来源网络,侵删)

你可以把它想象成是 Node.js 程序的“大脑”或“控制中心”,无论你写的是多么简单的脚本,它都在一个 process 对象的“监视”下运行。

核心特点:

  1. 全局可用:你不需要 require('process') 就能直接使用它,在任何地方都可以访问 process
  2. 事件驱动process 对象本身就是一个 EventEmitter,它会在特定事件发生时触发事件,比如程序即将退出、收到信号等。
  3. 信息枢纽:它包含了所有关于运行环境、程序输入输出的关键信息。

为什么 process 如此重要?

对于初学者来说,你可能不会在每行代码中都用到 process,但理解它是从“写脚本”到“写真正的应用程序”的关键一步,它的主要用途包括:

  • 获取环境信息:比如你是在开发环境还是生产环境运行代码。
  • 处理命令行参数:让你的脚本可以接收外部输入,变得更灵活。
  • 控制程序的生命周期:优雅地处理程序退出、错误和中断。
  • 读写标准输入/输出:与终端进行交互。
  • 管理程序退出码:向操作系统或调用者(如 shell 脚本)表明程序是成功还是失败地结束。

核心属性和方法详解

我们来看一些 process 对象最常用、最重要的成员。

process基础教程
(图片来源网络,侵删)

process.argv - 命令行参数

当你从命令行运行一个 Node.js 脚本时,你提供的所有参数都会被存储在 process.argv 数组中。

  • argv[0]: 总是 Node.js 可执行文件的路径。
  • argv[1]: 总是被执行的 JavaScript 文件的路径。
  • argv[2] 及以后: 你自己传入的参数。

示例:

创建一个文件 args.js

// args.js
console.log('process.argv:', process.argv);
// 遍历所有用户传入的参数
console.log('\n--- 用户传入的参数 ---');
for (let i = 2; i < process.argv.length; i++) {
  console.log(`参数 ${i - 1}: ${process.argv[i]}`);
}

在终端中运行:

process基础教程
(图片来源网络,侵删)
node args.js hello world 123

输出结果:

process.argv: [
  '/path/to/your/node', // 你的 node 路径
  '/path/to/your/args.js', // 你的文件路径
  'hello',
  'world',
  '123'
]
--- 用户传入的参数 ---
参数 1: hello
参数 2: world
参数 3: 123

process.env - 环境变量

process.env 是一个对象,包含了所有在启动 Node.js 进程时设置的环境变量,这是管理不同环境(开发、测试、生产)配置的最佳实践。

示例:

在终端中设置一个环境变量,然后运行脚本。

# 在 Linux 或 macOS 中
MY_NAME="Alice" node env.js
# 在 Windows PowerShell 中
$env:MY_NAME="Alice" ; node env.js
# 在 Windows CMD 中
set MY_NAME=Alice && node env.js

创建一个文件 env.js

// env.js
console.log('process.env.NODE_ENV:', process.env.NODE_ENV); // 常见的环境变量
console.log('process.env.MY_NAME:', process.env.MY_NAME); // 我们自定义的变量
// 如果没有设置,可以提供一个默认值
const dbHost = process.env.DB_HOST || 'localhost';
console.log('数据库连接地址:', dbHost);

输出结果:

process.env.NODE_ENV: undefined
process.env.MY_NAME: Alice
数据库连接地址: localhost

process.cwd()__dirname - 工作目录与文件目录

  • process.cwd(): 返回 Node.js 进程的当前工作目录,这个目录是你在终端中执行 node 命令时所在的目录。
  • __dirname: 一个全局变量(注意是两个下划线),返回当前 被执行的脚本所在的目录

示例:

假设你的文件结构如下:

/my-project
├── scripts
│   └── path.js
└── package.json

进入 /my-project 目录并运行 node scripts/path.js

创建 scripts/path.js

// scripts/path.js
console.log('当前进程的工作目录 (process.cwd()):', process.cwd());
console.log('当前脚本所在的目录 (__dirname):', __dirname);

输出结果:

当前进程的工作目录 (process.cwd()): /my-project
当前脚本所在的目录 (__dirname): /my-project/scripts

process.exit([code]) - 退出进程

这个方法会立即终止 Node.js 进程。

  • code 是一个可选的退出码,如果省略,默认是 0,表示成功退出,非零值(如 1)通常表示发生了错误。

示例:

创建 exit.js

// exit.js
console.log('程序开始...');
// 模拟一个错误
const hasError = true;
if (hasError) {
  console.log('发生严重错误,程序将退出。');
  process.exit(1); // 以错误码 1 退出
}
console.log('程序正常结束。'); // 这行代码不会执行

在终端中运行并检查退出码:

node exit.js
echo $?  # 在 Linux/macOS 中查看上一个命令的退出码
# 或者
echo %ERRORLEVEL% # 在 Windows CMD 中查看

你会看到 程序开始...发生严重错误... 被打印,echo $? 会输出 1

process.on('exit', ...)process.on('uncaughtException', ...) - 事件监听

process 对象是事件发射器,可以监听一些关键事件。

  • 'exit' 事件:在 Node.js 即将退出事件循环(event loop)之前触发。注意:在这个事件监听器中,只能执行同步操作,不能执行异步操作(如 setTimeout),因为进程即将退出。

  • 'uncaughtException' 事件:当一个未捕获的 JavaScript 异常冒泡到事件循环顶层时触发,这相当于一个“最后的救命稻草”,可以防止进程直接崩溃。最佳实践:在监听此事件后,你应该记录错误并调用 process.exit(1) 来退出进程,而不是让程序继续在一个未知状态下运行。

示例:

创建 events.js

// events.js
// 1. 监听 'exit' 事件
process.on('exit', (code) => {
  // 这是一个同步操作
  console.log(`进程即将退出,退出码是: ${code}`);
});
// 2. 监听 'uncaughtException' 事件
process.on('uncaughtException', (err) => {
  // 同样,这里也应该是同步操作
  console.error('捕获到一个未处理的异常!');
  console.error(err.stack);
  // 最好还是退出进程
  process.exit(1);
});
// 3. 正常的 'SIGINT' 信号监听 (按下 Ctrl+C)
process.on('SIGINT', () => {
  console.log('\n收到 SIGINT 信号 (Ctrl+C),正在优雅地关闭...');
  // 在这里执行清理操作,然后退出
  process.exit();
});
console.log('程序正在运行...');
// 故意制造一个未捕获的异常
setTimeout(() => {
  throw new Error('这是一个故意的未捕获异常!');
}, 1000);
console.log('这段代码会在异常发生前打印。');

在终端中运行:

node events.js

你会看到:

  1. 程序正在运行...
  2. 这段代码会在异常发生前打印。
  3. 大约1秒后,会打印 捕获到一个未处理的异常! 和错误堆栈。
  4. 然后进程退出,并打印 进程即将退出,退出码是: 1

实战应用:创建一个简单的命令行工具

让我们用学到的知识创建一个简单的 CLI 工具,它可以根据参数执行不同的操作。

目标:

  • node cli.js --name YourName -> 输出 Hello, YourName!
  • node cli.js --task list -> 输出 正在执行任务: list
  • 如果参数不正确,输出帮助信息。

创建 cli.js

// cli.js
// 1. 获取用户传入的所有参数
const args = process.argv.slice(2); // 去掉前两个固定参数
// 2. 如果没有参数,打印帮助信息并退出
if (args.length === 0) {
  console.log('用法:');
  console.log('  node cli.js --name <你的名字>');
  console.log('  node cli.js --task <任务名>');
  process.exit(0);
}
// 3. 解析参数
// 我们使用一个简单的循环来查找 --name 和 --task
let name = '';
let task = '';
for (let i = 0; i < args.length; i++) {
  if (args[i] === '--name' && i + 1 < args.length) {
    name = args[i + 1];
    i++; // 跳过下一个参数,因为它已经被当作值读取了
  } else if (args[i] === '--task' && i + 1 < args.length) {
    task = args[i + 1];
    i++; // 同上
  }
}
// 4. 根据解析到的参数执行逻辑
if (name) {
  console.log(`Hello, ${name}!`);
} else if (task) {
  console.log(`正在执行任务: ${task}`);
} else {
  console.log('错误: 参数无效,请使用 --name 或 --task。');
  process.exit(1);
}

测试:

# 测试 --name
node cli.js --name Alice
# 输出: Hello, Alice!
# 测试 --task
node cli.js --task build
# 输出: 正在执行任务: build
# 测试无参数
node cli.js
# 输出用法说明
# 测试无效参数
node cli.js --foo
# 输出: 错误: 参数无效,请使用 --name 或 --task。

成员/方法 描述 示例
process.argv 获取命令行参数数组。 node script.js arg1 arg2
process.env 获取环境变量对象。 process.env.NODE_ENV
process.cwd() 获取当前进程的工作目录。 process.cwd()
__dirname 获取当前脚本所在目录。 __dirname
process.exit([code]) 以指定码退出进程。 process.exit(1)
process.on('event', ...) 监听进程事件。 process.on('exit', ...)
process.stdin 标准输入流。 process.stdin.pipe(process.stdout)
process.stdout 标准输出流。 console.log 的底层就是它
process.stderr 标准错误流。 console.error 的底层就是它

process 是 Node.js 中一个非常基础且强大的模块,掌握它是成为一名合格 Node.js 开发者的必经之路,从今天起,尝试在写脚本时有意识地使用 process,你会发现你的程序变得更健壮、更灵活了。