Skip to content

Node系列 · Node基础:基础内置模块

Node 内置的核心模块里,os / path / url / util 是"几乎每个项目都会用"的四件套。它们不依赖任何第三方包,跨平台行为一致;理解它们的真实行为能避免一类经典的"在我电脑能跑"问题。

一、os:与操作系统交互

os 模块提供操作系统层面的查询能力——CPU、内存、用户目录、网络接口等。常用 API:

API返回类型用途
os.EOLstring当前系统的行结束符(\n / \r\n
os.arch()stringCPU 架构('arm64' / 'x64'
os.cpus()array每颗逻辑 CPU 核心信息
os.freemem()number空闲内存字节数
os.totalmem()number总内存字节数
os.homedir()string当前用户目录
os.hostname()string主机名
os.tmpdir()string系统临时目录
os.platform()string平台标识('darwin' / 'linux' / 'win32'
os.uptime()number系统启动到现在的秒数
os.networkInterfaces()object网络接口列表

典型应用:

javascript
const os = require('node:os');
const path = require('node:path');
const fs = require('node:fs');

// 根据 CPU 核心数决定要不要起 worker
const cpuCount = os.cpus().length;
const workers = Math.max(1, cpuCount - 1);

// 把日志写到系统临时目录
const logDir = path.join(os.tmpdir(), 'my-app-logs');
fs.mkdirSync(logDir, { recursive: true });

WARNING

不要拿 os.cpus().length 当真实物理核心数。它返回的是逻辑核心数(含超线程)。Node 单进程只能用一个 CPU 核心,物理多核需要 Worker Threads 或多进程。

二、path:跨平台路径处理

path 模块的核心价值是屏蔽 Windows / POSIX 路径差异。Node 代码里永远不要直接拼字符串路径,全部走 path API。

2.1 核心 API

API作用示例
path.basename(p)取文件名path.basename('/a/b/c.txt')'c.txt'
path.dirname(p)取目录path.dirname('/a/b/c.txt')'/a/b'
path.extname(p)取后缀path.extname('/a/b/c.txt')'.txt'
path.join(...p)拼接多段路径path.join('/a', 'b', 'c.txt')'/a/b/c.txt'
path.resolve(...p)解析为绝对路径path.resolve('c.txt')<cwd>/c.txt
path.relative(from, to)计算相对路径path.relative('/a/b', '/a/c/d')'../c/d'
path.isAbsolute(p)是否绝对路径path.isAbsolute('/a')true
path.normalize(p)规范化路径path.normalize('/a//b/./c')'/a/b/c'
path.sep当前系统的路径分隔符Linux \\ (实际 /) / Windows \
path.delimiter环境变量分隔符Linux : / Windows ;

2.2 join vs resolve

两个看着像,行为差异很大:

javascript
// join:纯拼接,结果是不是绝对路径看输入
path.join('/a', 'b', 'c.txt');   // '/a/b/c.txt'
path.join('a', 'b', 'c.txt');    // 'a/b/c.txt'

// resolve:从右往左拼,遇到绝对路径就重置起点
path.resolve('a', 'b', 'c.txt');    // <cwd>/a/b/c.txt
path.resolve('/a', 'b', '/c.txt');  // '/c.txt'(遇到 /c.txt 重置)

2.3 跨平台注意事项

javascript
// ❌ 错误:直接拼字符串
const filePath = __dirname + '/config/' + filename;

// ✅ 正确:用 path.join
const filePath = path.join(__dirname, 'config', filename);

// ❌ 错误:硬编码分隔符
const tmpPath = '/tmp/' + name;

// ✅ 正确:用 os.tmpdir() + path.join
const tmpPath = path.join(os.tmpdir(), name);

三、url:URL 解析与构造

url 模块提供 WHATWG URL 标准实现(Node 10+)。

3.1 核心 API

API用途
new URL(input, base?)构造一个 URL 对象
URLSearchParamsURL 查询字符串解析
url.fileURLToPath(url)file:// URL → 路径
url.pathToFileURL(path)路径 → file:// URL

3.2 URL 对象

javascript
const u = new URL('https://user:pass@example.com:8080/path/to?x=1&y=2#hash');

u.protocol;    // 'https:'
u.host;        // 'example.com:8080'
u.hostname;    // 'example.com'
u.port;        // '8080'
u.pathname;    // '/path/to'
u.search;      // '?x=1&y=2'
u.hash;        // '#hash'
u.username;    // 'user'
u.password;    // 'pass'
u.origin;      // 'https://example.com:8080'

3.3 查询参数

javascript
const u = new URL('https://example.com/api?x=1&y=2');

// 读取
u.searchParams.get('x');           // '1'
u.searchParams.getAll('x');        // ['1']
u.searchParams.has('z');           // false

// 增删改
u.searchParams.append('z', '3');
u.searchParams.set('x', '10');
u.searchParams.delete('y');

// 序列化
u.toString();                       // 'https://example.com/api?x=10&z=3'

// 单独构造
const params = new URLSearchParams({ foo: '1', bar: '2' });
params.toString();                  // 'foo=1&bar=2'

3.4 路径与 URL 互转

javascript
import { fileURLToPath, pathToFileURL } from 'node:url';

// path → file:// URL
pathToFileURL('/usr/local/bin');    // 'file:///usr/local/bin'

// file:// URL → path
fileURLToPath('file:///usr/local/bin'); // '/usr/local/bin'

ESM 模块下 import.meta.urlfile:// URL,要拿路径必须 fileURLToPath 转一次(CJS 下直接是 __dirname)。

四、util:工具函数集合

util 模块聚集了各种"杂项但有用"的工具。

4.1 类型判断

javascript
util.isArray([]);                  // true
util.isDate(new Date());           // true
util.isRegExp(/x/);                // true
util.types.isPromise(Promise.resolve()); // true
util.types.isMap(new Map());       // true
util.types.isSet(new Set());       // true
util.types.isArrayBuffer(new ArrayBuffer(8)); // true

TIP

Node 10+ 之后 Array.isArray / instanceof 已经够用,util.isArray 等被视为遗留 API。新代码建议用 util.types 或原生 Array.isArray

4.2 回调与 Promise 互转

javascript
// 旧的 CJS 回调风格 API:(err, value) => {...}
// 想用 async/await?util.promisify 把它包成返回 Promise 的函数

const fs = require('fs');
const readFile = util.promisify(fs.readFile);

const data = await readFile('./config.json', 'utf-8');

// 反向:Promise 风格 API 想给老代码用?util.callbackify
const asyncAdd = async (a, b) => a + b;
const cbAdd = util.callbackify(asyncAdd);

cbAdd(1, 2, (err, sum) => {
  console.log(sum); // 3
});

4.3 继承(inherits

javascript
// ES6 class 时代几乎不用——直接用 extends 即可
// 留给老代码理解

function Animal() {}
Animal.prototype.greet = function () { return 'hello'; };

function Dog() {}
util.inherits(Dog, Animal);
Dog.prototype.bark = function () { return 'woof'; };

const d = new Dog();
d.greet(); // 'hello'
d.bark();  // 'woof'

4.4 深度严格比较

javascript
util.isDeepStrictEqual(
  { a: 1, b: [1, 2] },
  { a: 1, b: [1, 2] }
); // true

// 与 '===' 的关键区别:递归比较对象、数组、Map、Set
1 === 1;                           // true
{ a: 1 } === { a: 1 };             // false
util.isDeepStrictEqual(
  { a: 1 }, { a: 1 }
); // true

4.5 调试输出

javascript
const obj = { a: 1, b: { c: [1, 2, 3] } };
console.log(util.inspect(obj, { depth: 4, colors: true }));

util.inspectconsole.log 内部实现,可以指定深度、颜色、隐藏字段等。调试复杂对象时比直接 JSON.stringify 信息更全(保留函数、undefined、循环引用)。

五、其他常用内置模块速览

模块用途备注
querystringURL 查询字符串URLSearchParams 已覆盖大部分场景
assert断言(单元测试)Node 自带测试时用,jest 流行后少用
eventsEventEmitterEventEmitter
stream流处理文件流
crypto加密 / hash见后续章节
zlib压缩 / 解压gzip / deflate
child_process子进程spawn / exec / fork

六、最佳实践

场景推荐做法反例
拼接文件路径path.join / path.resolve字符串 +
读用户目录os.homedir()假设是 /home/x
写临时文件os.tmpdir() + path.join假设是 /tmp
解析 URLnew URL(...) + searchParams手写 split
老 API 转 Promiseutil.promisify自己手写 wrapper
判断内置类型util.typesinstanceof 跨 realm 不可靠
多行字符串拼接path.joinos.EOL硬编码 \n / \r\n

七、小结

  • os 提供系统信息查询;os.cpus().length 是逻辑核心数,含超线程
  • path 是跨平台路径处理的唯一正确选择;join 拼接、resolve 解析为绝对路径
  • url 用 WHATWG 标准;new URL + searchParams 处理查询参数
  • util.promisify 把回调 API 包成 Promise;util.callbackify 反向
  • 这些模块零依赖——新项目能少装一个包就少装一个