Node.js 全局对象详解

在 Node.js 中,有一些对象和变量可以在任何地方访问,无需使用 require() 引入。它们被称为全局对象(Global Objects)。但需要注意的是,其中一些变量(如 __dirname)实际上是每个模块作用域内的局部变量,但行为上类似于全局变量。本章将详细介绍这些全局对象及其用法。

1. global 对象

在浏览器中,全局对象是 window;在 Node.js 中,全局对象是 global。所有全局变量(除了模块自身的变量)都是 global 对象的属性。例如:

console.log(global === globalThis); // true(在大多数环境下)

// 在 REPL 中,var 声明的变量会成为 global 的属性
var foo = 'bar';
console.log(global.foo); // 输出 'bar'

// 但在模块文件中,var 声明的变量只在模块内部,不会添加到 global

从 ECMAScript 2020 开始,globalThis 提供了跨平台访问全局对象的标准方式,无论在浏览器还是 Node.js 中都能使用。

global 的常用属性

  • global.console - 控制台对象
  • global.Buffer - 处理二进制数据的类
  • global.setTimeout/clearTimeout - 定时器函数
  • global.setInterval/clearInterval
  • global.setImmediate/clearImmediate - 立即执行(Node.js 特有)
  • global.process - 进程对象
  • global.URL - URL 解析类(需要引入 url 模块,但也可全局访问)
注意: 在模块中,直接使用 varlet 声明的变量不会自动成为 global 的属性,它们仅作用于当前模块。要显式创建全局变量,可以将其添加为 global 的属性(例如 global.myVar = 123),但应避免这样做,以免污染全局命名空间。

2. process 对象

process 是一个非常重要的全局对象,它提供当前 Node.js 进程的信息和控制能力。常用属性和方法:

2.1 进程信息

console.log(process.pid);          // 进程 ID
console.log(process.platform);      // 操作系统平台 'win32', 'darwin', 'linux' 等
console.log(process.arch);          // CPU 架构 'x64', 'arm' 等
console.log(process.version);       // Node.js 版本号
console.log(process.versions);      // 各依赖版本(v8, uv, zlib 等)
console.log(process.cwd());         // 当前工作目录
console.log(process.execPath);      // Node.js 可执行文件的路径

2.2 环境变量

process.env 包含用户环境变量。可以读取和设置(但设置只影响当前进程,不会影响系统):

console.log(process.env.PATH);      // 获取 PATH 环境变量
process.env.MY_VAR = 'hello';       // 临时设置环境变量

常用于配置(如通过 NODE_ENV 区分开发/生产环境)。

2.3 命令行参数

process.argv 返回一个数组,包含启动 Node.js 进程时的命令行参数。第一个元素是 Node.js 可执行文件路径,第二个是当前执行的 JavaScript 文件路径,后续是自定义参数。

// 运行 node app.js a=1 b=2
console.log(process.argv);
// 输出类似:
// [
//   '/usr/bin/node',
//   '/path/to/app.js',
//   'a=1',
//   'b=2'
// ]

2.4 进程控制

  • process.exit([code]) - 终止进程,可指定退出码(0 表示成功,非 0 表示异常)。
  • process.nextTick(callback) - 将回调推迟到当前操作完成后、事件循环继续之前执行。比 setImmediate 优先级高。
  • process.on('事件', callback) - 监听进程事件,如 exituncaughtExceptionSIGINT 等。
process.on('exit', (code) => {
  console.log(`进程退出,退出码: ${code}`);
});

process.on('uncaughtException', (err) => {
  console.error('未捕获的异常:', err);
  process.exit(1);
});

3. console 对象

console 用于向标准输出(stdout)或标准错误(stderr)打印信息。常用方法:

console.log('普通日志');           // 换行输出
console.error('错误信息');         // 输出到 stderr
console.warn('警告');              // 同 error
console.info('信息');              // 同 log

const obj = { name: 'Node', type: 'runtime' };
console.dir(obj, { depth: 1, colors: true }); // 以格式化的方式打印对象

console.time('计时器');
// 执行一些操作
console.timeEnd('计时器');          // 输出耗时

console.trace('跟踪');              // 打印当前位置的堆栈跟踪

4. Buffer 类

Buffer 用于处理二进制数据(如文件 I/O、网络通信)。它是 Uint8Array 的子类,提供了一系列操作字节的方法。在 Node.js 中,Buffer 是全局可用的。

// 创建 Buffer
const buf1 = Buffer.alloc(10);                     // 创建一个长度为10的零填充缓冲区
const buf2 = Buffer.from('Hello', 'utf8');          // 从字符串创建
const buf3 = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]); // 从字节数组创建

console.log(buf2.toString('utf8'));                 // 输出 'Hello'
console.log(buf2[0]);                                // 输出 104('h' 的 ASCII 码)

// 拼接 Buffer
const buf4 = Buffer.concat([buf2, Buffer.from(' World')]);
console.log(buf4.toString());                        // 'Hello World'

5. 定时器函数

Node.js 实现了与浏览器相似的定时器 API,但略有差异(如 setImmediate 是 Node.js 特有的)。

  • setTimeout(callback, delay[, ...args]) - 延迟执行回调。
  • setInterval(callback, delay[, ...args]) - 周期性执行回调。
  • setImmediate(callback[, ...args]) - 在当前事件循环的末尾执行回调(立即执行,但比 nextTick 晚)。
  • 对应的 clearTimeoutclearIntervalclearImmediate 用于取消定时器。
const timer = setTimeout(() => {
  console.log('2秒后执行');
}, 2000);
clearTimeout(timer); // 取消

setImmediate(() => {
  console.log('在当前事件循环末尾执行');
});

process.nextTick(() => {
  console.log('在当前操作完成后立即执行(比 setImmediate 优先)');
});

6. 模块作用域的“全局”变量

以下变量在每个模块中都是可用的,但它们并不是真正的全局对象,而是每个模块的局部变量。然而,由于它们无处不在,通常也归类为“全局”。

6.1 __dirname 和 __filename

__dirname 返回当前模块所在的目录绝对路径。__filename 返回当前模块文件的绝对路径(包含文件名)。

console.log(__dirname);  // 例如 /home/user/project
console.log(__filename); // 例如 /home/user/project/app.js

6.2 exports, module, require()

  • exportsmodule.exports 的引用,用于导出模块的公共接口。
  • module 代表当前模块对象,其中 module.exports 是真正导出的内容。
  • require() 用于引入其他模块、JSON 文件或内置模块。
// 导出
exports.sayHello = () => console.log('Hello');
// 等价于
module.exports.sayHello = () => console.log('Hello');

// 引入
const myModule = require('./myModule');

7. 其他全局对象

根据 Node.js 版本,还可能存在以下全局对象(或通过 global 访问):

  • URLURLSearchParams - 用于 URL 解析和操作(需要引入 url 模块,但在较新版本中也可全局访问)。
  • TextEncoderTextDecoder - 用于处理字符串和 Uint8Array 之间的编码转换。
  • performance - 提供高精度时间测量(类似浏览器中的 performance.now())。
  • atobbtoa - Base64 编解码(需要引入 buffer 或全局可用?)。

这些对象可能不是在所有版本中都默认全局,但可以通过 require() 引入对应的模块使用。

最佳实践: 尽量避免给 global 对象添加属性,除非你有充分的理由(例如在 polyfill 或调试工具中)。显式依赖导入会使代码更清晰、更易于维护和测试。

8. 全局对象与严格模式

在模块文件中,默认启用严格模式("use strict")。在严格模式下,未声明的变量赋值会抛出错误,而且 var 声明的变量不会成为 global 的属性。这有助于避免意外创建全局变量。

小结

本章详细介绍了 Node.js 中的各种全局对象和变量:globalprocessconsoleBuffer、定时器函数,以及模块作用域的 __dirnamerequire 等。理解这些全局对象对于编写 Node.js 应用程序至关重要。在下一章中,我们将深入探讨 Node.js 的模块系统,学习如何组织和复用代码。