首页 / Node.js 教程 / 全局对象

Node.js 教程

全局对象

本教程共 76 篇 · 第 24 篇 · 更新于 2026-07-25 · 约 5 分钟阅读

Node.jsglobalprocess环境变量__dirname

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

把一个回调塞到当前事件循环的最前面,比 setImmediatesetTimeout(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.jsfileURLToPath 把它转成本地路径,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 工具里进度条的基本原理。