全局对象
本教程共 76 篇 · 第 24 篇 · 更新于 2026-07-25 · 约 5 分钟阅读
24. 全局对象
本节目标:process、globalThis、__dirname 等全局对象和环境变量。
浏览器里全局对象是 window,Node.js 里是 global。但不同环境的命名不统一,ES2020 引入了 globalThis 来统一这个混乱局面。除此之外,process 对象让你能和当前运行进程深度交互,console 则是调试时最亲密的伙伴。
24.1 globalThis
globalThis 是唯一跨平台的全局对象引用:浏览器里是 window,Worker 里是 self,Node.js 里是 global。
// 在 Node.js 中以下三者等价
globalThis === global; // true
globalThis.console === console; // true
// 定义全局变量(不推荐,但要知道)
globalThis.myConfig = { env: 'production' };
Warning往全局对象上挂变量是污染命名空间的做法。模块系统(ESM / CommonJS)就是为了避免这个。除非写测试桩或 polyfill,否则别这么干。
24.2 process:进程信息控制中心
process 对象不需要导入,直接使用。它是 Node.js 和操作系统对话的主要接口。
命令行参数
// node app.js --port 3000 --env prod
console.log(process.argv);
// ['node', '/path/to/app.js', '--port', '3000', '--env', 'prod']
argv[0] 是 Node 可执行文件路径,argv[1] 是脚本路径,从 argv[2] 开始才是真正的用户参数。实际项目中建议用 util.parseArgs(v18.3+)或 minimist 解析:
import { parseArgs } from 'node:util';
const { values } = parseArgs({
options: {
port: { type: 'string', default: '3000' },
env: { type: 'string' }
},
strict: false
});
console.log(values.port); // '3000'
环境变量
// 读取环境变量
const dbHost = process.env.DB_HOST || 'localhost';
const dbPort = parseInt(process.env.DB_PORT || '5432', 10);
// 写入(仅当前进程及其子进程可见)
process.env.NODE_ENV = 'production';
环境变量是配置敏感信息(数据库密码、API 密钥)的标准做法,别往代码仓库里写死。
进程退出
process.exit(1); // 非零退出码表示异常
直接调用 process.exit 比较粗暴,事件循环里排队的异步任务会被砍掉。更好的做法是给进程发信号,让它自己优雅关闭:
function shutdown(signal) {
console.log(`收到 ${signal},开始清理...`);
// 关闭数据库连接、停止定时器...
process.exit(0);
}
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
SIGTERM 是 Docker、systemd 停止容器时的默认信号;SIGINT 对应你按下的 Ctrl+C。
标准 I/O
process.stdout.write('不带换行的输出');
process.stderr.write('错误信息\n');
// 从标准输入读取
process.stdin.on('data', chunk => {
console.log('输入:', chunk.toString());
});
console.log 本质上就是 process.stdout.write 加了换行和格式化。
进程信息
console.log(process.pid); // 进程 ID
console.log(process.ppid); // 父进程 ID
console.log(process.platform); // win32 / linux / darwin
console.log(process.arch); // x64 / arm64
console.log(process.version); // Node.js 版本,如 v24.18.0
console.log(process.versions); // V8、libuv、OpenSSL 等组件版本
console.log(process.cwd()); // 当前工作目录
console.log(process.title); // 进程名(可修改)
process.nextTick
把一个回调塞到当前事件循环的最前面,比 setImmediate 和 setTimeout(0) 都快:
console.log('1');
process.nextTick(() => console.log('2'));
setImmediate(() => console.log('3'));
setTimeout(() => console.log('4'), 0);
console.log('5');
// 输出顺序: 1, 5, 2, 4, 3(setTimeout 和 setImmediate 顺序不绝对)
nextTick 虽然快,但滥用会导致 I/O 饥饿——如果不停地 nextTick 塞任务,事件循环永远轮不到后面的阶段。需要 defer 但不急到下一 tick 时,用 setImmediate 更友好。
24.3 console 的进阶用法
console 不只是 .log 和 .error。
计时
console.time('查询');
await db.query('SELECT * FROM users');
console.timeEnd('查询'); // 查询: 45.2ms
表格与断言
console.table([
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
]);
console.assert(1 === 2, '这行会打印,因为断言失败');
trace:打印调用栈
function inner() {
console.trace('追踪到这里');
}
function outer() {
inner();
}
outer();
dir:深度展开对象
const obj = { a: { b: { c: { d: 1 } } } };
console.dir(obj, { depth: null, colors: true });
24.4 __dirname / __filename 与 import.meta.url
CommonJS 模块里可以直接用 __dirname 和 __filename:
// CommonJS
console.log(__filename); // /project/src/app.js
console.log(__dirname); // /project/src
ES 模块里没有这两个变量,因为 ES 模块规范本身没定义它们。替代方案是 import.meta.url:
// ESM
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import.meta.url 返回当前模块的 URL,比如 file:///project/src/app.js。fileURLToPath 把它转成本地路径,dirname 再提取目录部分。
如果你不想每个文件都写这几行,可以封装一个工具模块:
// utils/dirname.js
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
export function getDirname(metaUrl) {
return dirname(fileURLToPath(metaUrl));
}
// app.js
import { getDirname } from './utils/dirname.js';
const __dirname = getDirname(import.meta.url);
24.5 实战:命令行进度显示
用 process.stdout.write 和控制字符做一个简单的下载进度条:
function progressBar(current, total, width = 40) {
const ratio = current / total;
const filled = Math.floor(ratio * width);
const empty = width - filled;
const bar = '█'.repeat(filled) + '░'.repeat(empty);
const percent = (ratio * 100).toFixed(1);
process.stdout.write(`\r[${bar}] ${percent}%`);
if (current >= total) process.stdout.write('\n');
}
// 模拟下载进度
let downloaded = 0;
const total = 1000;
const timer = setInterval(() => {
downloaded += Math.floor(Math.random() * 50);
if (downloaded > total) downloaded = total;
progressBar(downloaded, total);
if (downloaded >= total) clearInterval(timer);
}, 50);
\r 把光标拉回行首,实现原地刷新效果。这是 CLI 工具里进度条的基本原理。