Node.js 的全局对象分两类:真正全局(CJS/ESM 通用)约 13 个,伪全局(仅 CommonJS 可见)5 个。真正全局里最常用的是 process、console、Buffer、定时器四件套、WHATWG Web API;伪全局则是 __dirname、__filename、require、module、exports 这一组老朋友。下面把每个对象的作用、典型用法、踩坑提醒三件事一次说清。
真正全局:跨模块、跨标准都可用
这些对象在 CommonJS 和 ESM 里都不需要 import,直接用即可。它们都挂在 globalThis 上,浏览器兼容的部分还能在 Deno、Bun 里复用。
global / globalThis
Node 没有浏览器的 window,globalThis 就是全局命名空间的根,global 是它的历史别名。新代码统一用 globalThis(ES2020 标准),从 Node 12 起原生支持。
// 任何模块、任何位置直接访问
console.log(globalThis.process === process); // true
globalThis.__APP_START__ = Date.now();
注意:向 globalThis 挂自定义变量会产生跨模块状态污染,多个测试用例之间可能互相干扰。非要共享,建议用 Symbol.for('appName') 命名或单独建一个配置模块。
process
当前 Node 进程的单例对象。日常用得最多的成员:
process.env:环境变量(process.env.NODE_ENV、process.env.PORT,全部是字符串)process.argv:命令行参数数组process.cwd():当前工作目录(与__dirname不一定相同)process.exit(code):退出进程,code=0正常退出process.on('uncaughtException', cb)/process.on('unhandledRejection', cb):捕获未处理异常与 Promise 拒绝process.nextTick(cb):在事件循环微任务之前插入回调,慎用
process.env.PORT 永远返回字符串或 undefined,做算术前记得 Number() 强转,否则 "3000" + 1 会变成 "30001"。
console
标准输出与标准错误的封装。常用方法:
console.log / info / warn / error / debug:分别走 stdout / stderrconsole.time(label)与console.timeEnd(label):性能计时console.table(obj):把对象/数组以表格形式打印console.trace():打印当前位置的调用栈
Node 里 console.log 写入 TTY 时是同步的,写入文件或管道时是异步的,调试高并发日志要留意——别在大循环里靠 console.log 限流,没用。
Buffer
V8 堆外的一段固定长度二进制内存。两种创建方式:
const a = Buffer.alloc(8); // 8 字节的零填充
const b = Buffer.from('hello'); // 从字符串创建(默认 UTF-8)
const c = Buffer.from([0x68, 0x69]); // 从字节数组创建
fs 与 net 模块默认返回 Buffer,转字符串用 buf.toString('utf8')。Buffer 早于 Uint8Array 出现,Node 里仍是事实标准;新代码里跟标准交互的部分建议用 Uint8Array(Buffer 是它的子类,混用基本无障碍)。
setTimeout / setInterval / setImmediate 与对应的 clear* 系列
定时器四件套,行为各有不同:
setTimeout(cb, ms):最少ms毫秒后执行一次(受系统调度粒度影响,实际通常 ≥1ms)setInterval(cb, ms):按ms间隔重复执行(受事件循环繁忙度影响,实际间隔可能更大)setImmediate(cb):当前事件循环迭代结束、I/O 回调之后立即执行setTimeout(cb, 0):等价于setImmediate吗?不完全等价——setTimeout(0)最小延迟 1ms,setImmediate严格在 I/O 之后
返回值都是 Timeout 对象,传给对应 clear* 可取消。在 I/O 密集场景里优先 setImmediate 避免定时器被饿死。
queueMicrotask(cb)
把回调插入 V8 微任务队列。语义上等价于 Promise.resolve().then(cb),但不需要包装 Promise,常用于确保回调异步执行且顺序可控。
注意:process.nextTick 优先级高于微任务队列,过度使用会造成「nextTick 饥饿」、阻塞 I/O。新代码优先用 queueMicrotask,只在确实需要在微任务之前插入时才用 nextTick。
URL / URLSearchParams / URLPattern
WHATWG URL 标准实现,从 Node 10 起逐步引入。
const u = new URL('https://a.com/b?x=1&y=2');
console.log(u.host, u.pathname, u.searchParams.get('x'));
// 'a.com' '/b' '1'
const pat = new URLPattern('/api/:id', 'https://a.com');
console.log(pat.test('https://a.com/api/42')); // true
URLSearchParams 处理查询串、URLPattern 声明路由模式(Node 23 实验性),都是真正全局。
TextEncoder / TextDecoder
WHATWG 文本编解码。TextEncoder 把字符串编码为 Uint8Array,默认 UTF-8;TextDecoder 反向解码并支持 stream: true 流式解码。
跟 Buffer 配合完成字符串与二进制的互转:
const enc = new TextEncoder().encode('hi'); // Uint8Array
const dec = new TextDecoder().decode(enc); // 'hi'
fetch / Request / Response / Headers / FormData
浏览器兼容 HTTP 实现。从 Node 18 起 fetch 升级为真正全局,底层由 undici 实现。除了发请求,服务端也能直接构造 Response 对象返回自定义响应,常用于 Service Worker 风格的中间件或测试桩。
const r = await fetch('https://api.example.com/data');
const j = await r.json();
配套的 AbortController / AbortSignal 也全局化了,可取消 fetch 与其他基于 Promise 的操作。
structuredClone(value)
浏览器兼容深拷贝。从 Node 17 起全局可用,能克隆大多数 JS 值(不包括函数、Symbol、原型链上的 DOM 节点),是 JSON.parse(JSON.stringify(obj)) 的安全替代——后者会丢 Date、丢 undefined、丢循环引用。
const a = { x: new Date(), y: [1,2], z: undefined };
const b = structuredClone(a); // 完全独立副本
Serverless 场景下常用它跨异步上下文传递深嵌套数据。
performance
性能时间线 API。performance.now() 返回亚毫秒精度相对时间(自进程启动起),比 Date.now() 更适合做耗时测量。
PerformanceObserver 可订阅各种 PerformanceEntry,对性能监控与告警有用。底层是 perf_hooks 模块,但 performance 本身已全局化。
WebAssembly
浏览器兼容编译执行容器。WebAssembly.instantiate(bufferSource) 在 Node 里加载 .wasm 二进制并执行,可与 C/C++/Rust 写的模块互通,常用于性能敏感计算(图像处理、加密、压缩)。
const buf = await fs.promises.readFile('module.wasm');
const { instance } = await WebAssembly.instantiate(buf);
console.log(instance.exports.add(1, 2)); // 3
navigator
部分浏览器 API 适配,便于同一份代码同时在浏览器和 Node 运行。常用属性:
navigator.userAgent:'Node.js/22.9.0'之类navigator.hardwareConcurrency:逻辑 CPU 数navigator.languages:默认语言列表
不强求使用,只是跟浏览器代码兼容时方便。
伪全局:仅在 CommonJS 中可见
Node 启动 CommonJS 文件时,会用 (function(exports, require, module, __filename, __dirname) { ... }) 把源码包起来,于是这 5 个变量「看起来是全局」。在 ESM(.mjs 或 "type": "module")里它们全部不存在。
__dirname
当前模块文件所在目录的绝对路径。注意跟 process.cwd() 区别:__dirname 跟着文件走、cwd() 跟着执行命令的目录走,两者经常不同。
ESM 替代:path.dirname(fileURLToPath(import.meta.url))。
__filename
当前模块文件的绝对路径,含文件名。
ESM 替代:fileURLToPath(import.meta.url)。
require(id)
同步加载模块,CommonJS 的核心。常用方法:
require.resolve(id):返回解析后的文件路径(不实际加载)require.cache:模块缓存对象,删键可强制重载(慎用)require.main:入口模块引用,可判断当前文件是否被node直接执行
ESM 替代:import ... from '...'(异步、静态分析、tree-shaking 友好)。
module
当前模块的引用。module.exports 设置模块对外暴露内容,是 CommonJS 的导出机制核心。
exports
module.exports 的简写形式。但给 exports 整体赋值为新对象会切断它与 module.exports 的连接——这是新手最常踩的坑:
// 错误:外部拿到的是 {}
exports = { foo: 1 };
// 正确:修改 exports 的属性
exports.foo = 1;
// 或:直接覆盖 module.exports
module.exports = { foo: 1 };
速查对照表
按”是否真正全局 × 是否支持 ESM”两个维度,把上面 18 个对象分四档:
| 对象 / 变量 | 类型 | 挂 globalThis | CJS 可用 | ESM 可用 | 引入版本 |
|---|---|---|---|---|---|
globalThis / global |
命名空间 | 是 | 是 | 是 | 0.1.27 / 12.0.0 |
process |
进程单例 | 是 | 是 | 是 | 0.1.7 |
console |
日志 | 是 | 是 | 是 | 0.1.100 |
Buffer |
二进制 | 是 | 是 | 是 | 0.1.103 |
setTimeout / setInterval / setImmediate |
定时器 | 是 | 是 | 是 | 0.0.1 / 0.9.1 |
queueMicrotask |
微任务 | 是 | 是 | 是 | 11.0.0 |
URL / URLSearchParams |
URL 解析 | 是 | 是 | 是 | 10.0.0 |
TextEncoder / TextDecoder |
编解码 | 是 | 是 | 是 | 11.0.0 |
fetch / Request / Response |
HTTP | 是 | 是 | 是 | 18.0.0 |
structuredClone |
深拷贝 | 是 | 是 | 是 | 17.0.0 |
performance |
时间 | 是 | 是 | 是 | 8.5.0(全局化 16.0.0) |
WebAssembly |
编译执行 | 是 | 是 | 是 | 8.0.0 |
navigator |
环境信息 | 是 | 是 | 是 | 21.0.0 |
__dirname / __filename |
路径 | 否 | 是 | 否 | — |
require / module / exports |
模块 | 否 | 是 | 否 | — |
常见问题(FAQ)
Q1:global 和 globalThis 有什么区别?
global 是 Node 专属全局命名空间,globalThis 是 ES2020 标准、跨环境(浏览器 / Node / Deno / Bun)通用的根对象。新代码统一用 globalThis,老代码用 global 也不影响。
Q2:ESM 里怎么拿到当前文件目录?
两步:import.meta.url 拿到当前文件的 URL,path.dirname(fileURLToPath(import.meta.url)) 把 URL 转成目录路径,赋给 __dirname 即可。
Q3:process.nextTick 和 queueMicrotask 该用哪个?
优先 queueMicrotask。process.nextTick 不在 V8 微任务队列中、也不受 I/O 调度约束,过度使用会阻塞事件循环;queueMicrotask 走标准微任务队列,行为可预测、与浏览器一致。