Node系列 · Node基础:基础内置模块
Node 内置的核心模块里,
os/path/url/util是"几乎每个项目都会用"的四件套。它们不依赖任何第三方包,跨平台行为一致;理解它们的真实行为能避免一类经典的"在我电脑能跑"问题。
一、os:与操作系统交互
os 模块提供操作系统层面的查询能力——CPU、内存、用户目录、网络接口等。常用 API:
| API | 返回类型 | 用途 |
|---|---|---|
os.EOL | string | 当前系统的行结束符(\n / \r\n) |
os.arch() | string | CPU 架构('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 对象 |
URLSearchParams | URL 查询字符串解析 |
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.url 是 file:// 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)); // trueTIP
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 }
); // true4.5 调试输出
javascript
const obj = { a: 1, b: { c: [1, 2, 3] } };
console.log(util.inspect(obj, { depth: 4, colors: true }));util.inspect 是 console.log 内部实现,可以指定深度、颜色、隐藏字段等。调试复杂对象时比直接 JSON.stringify 信息更全(保留函数、undefined、循环引用)。
五、其他常用内置模块速览
| 模块 | 用途 | 备注 |
|---|---|---|
querystring | URL 查询字符串 | URLSearchParams 已覆盖大部分场景 |
assert | 断言(单元测试) | Node 自带测试时用,jest 流行后少用 |
events | EventEmitter | 见 EventEmitter |
stream | 流处理 | 见 文件流 |
crypto | 加密 / hash | 见后续章节 |
zlib | 压缩 / 解压 | gzip / deflate |
child_process | 子进程 | spawn / exec / fork |
六、最佳实践
| 场景 | 推荐做法 | 反例 |
|---|---|---|
| 拼接文件路径 | path.join / path.resolve | 字符串 + 拼 |
| 读用户目录 | os.homedir() | 假设是 /home/x |
| 写临时文件 | os.tmpdir() + path.join | 假设是 /tmp |
| 解析 URL | new URL(...) + searchParams | 手写 split |
| 老 API 转 Promise | util.promisify | 自己手写 wrapper |
| 判断内置类型 | util.types | instanceof 跨 realm 不可靠 |
| 多行字符串拼接 | path.join 或 os.EOL | 硬编码 \n / \r\n |
七、小结
os提供系统信息查询;os.cpus().length是逻辑核心数,含超线程path是跨平台路径处理的唯一正确选择;join拼接、resolve解析为绝对路径url用 WHATWG 标准;new URL+searchParams处理查询参数util.promisify把回调 API 包成 Promise;util.callbackify反向- 这些模块零依赖——新项目能少装一个包就少装一个
