一、util 模块在 Node.js 中的定位
util 是 Node.js 的内置核心模块,定位是“实用工具库”,用来弥补 JavaScript 原生能力的不足,把类型判断、调试输出、回调与 Promise 互转、原型继承等常见编程任务封装成开箱即用的函数。在 Node.js 早期版本中,util.inherits、util.inspect 这类能力就属于该模块,后来部分功能被整合到其他模块中,但 util 依旧保留了相当多仍在日常开发中高频使用的工具函数。
使用时无需安装,直接引入即可:
- const util = require('util');
复制代码
按用途划分,util 模块的常用能力可以归为以下几类:
- 类型检查:util.types.*,用于精确判断数据类型
- 字符串格式化:util.format()、util.inspect()
- 回调与 Promise 互转:util.promisify()、util.callbackify()
- 原型继承:util.inherits()
- 调试输出:util.debuglog()、util.inspect()
- 废弃标记:util.deprecate()
二、类型检查:util.types 比 typeof / instanceof 更可靠
util.types 提供一组精确的类型判定方法,避免 typeof 对部分类型判断不准确、instanceof 受原型链影响的问题。下面示例演示 ArrayBuffer 与 Date 的判断:
- const util = require('util');
- // ArrayBuffer 检查
- const arrBuffer = new ArrayBuffer(8);
- console.log(util.types.isArrayBuffer(arrBuffer)); // true
- console.log(util.types.isArrayBuffer([])); // false
- // Date 检查
- console.log(util.types.isDate(new Date())); // true
- console.log(util.types.isDate('2023-01-01')); // false
复制代码
常用判定方法包括:isArrayBuffer、isAsyncFunction、isDate、isMap、isSet、isRegExp、isPromise、isTypedArray,分别用于判断对应类型的实例,写法统一,参数均为待检测的 value。
三、字符串格式化:util.format
util.format 的行为类似 C 语言的 printf,通过占位符把参数拼进模板字符串。支持的占位符中,%s 对应字符串,%d 对应数字,%j 对应 JSON 序列化后的对象:
- const util = require('util');
- const name = 'John';
- const age = 30;
- console.log(util.format('My name is %s and I am %d years old', name, age));
- // 输出: My name is John and I am 30 years old
- console.log(util.format('%s - %d - %j', 'string', 123, { key: 'value' }));
- // 输出: string - 123 - {"key":"value"}
复制代码
当需要在日志或错误信息中拼装结构化数据时,%j 比手工 JSON.stringify 更省事。
四、对象检查:util.inspect 与常用选项
util.inspect 返回对象的字符串表示,主要用于调试输出,相比直接 console.log 能更完整地呈嵌套结构与特殊类型。可配置的常用选项如下:
- depth:递归深度,null 表示无限层级
- colors:是否使用颜色输出
- showHidden:是否显示不可枚举属性
- compact:是否压缩输出
基础用法:
- const util = require('util');
- const obj = {
- name: 'Alice',
- details: {
- age: 25,
- hobbies: ['reading', 'coding']
- }
- };
- console.log(util.inspect(obj, { depth: 2, colors: true }));
复制代码
处理包含正则、日期、函数等成员的复杂对象时,把 depth 设为 null 并打开 showHidden 可以看到更完整的信息:
- const util = require('util');
- const complexObj = {
- date: new Date(),
- regex: /test/g,
- nested: {
- array: [1, 2, 3],
- fn: function() {}
- }
- };
- console.log(util.inspect(complexObj, {
- showHidden: true,
- depth: null,
- colors: true
- }));
复制代码
五、回调与 Promise 的互相转换
util.promisify 用于把遵循 Node.js error-first 回调风格的函数转换为返回 Promise 的函数,从而可以直接配合 async/await 使用。以 fs.readFile 为例:
- const util = require('util');
- const fs = require('fs');
- const readFile = util.promisify(fs.readFile);
- async function readConfig() {
- try {
- const data = await readFile('config.json', 'utf8');
- console.log(data);
- } catch (err) {
- console.error('Error reading file:', err);
- }
- }
- readConfig();
复制代码
util.callbackify 则是反向操作,把返回 Promise 的函数转换为回调风格函数,回调的第一个参数是错误:
- const util = require('util');
- async function asyncFunc() {
- return 'Hello World';
- }
- const callbackFunc = util.callbackify(asyncFunc);
- callbackFunc((err, result) => {
- if (err) throw err;
- console.log(result); // Hello World
- });
复制代码
这一对 API 的典型价值是把沉淀下来的旧回调代码接入新的 async/await 调用链,而不必重写内部实现。
六、原型继承:util.inherits
util.inherits 实现 ES5 风格的原型继承,把子构造函数与父构造函数关联起来:
- const util = require('util');
- function Animal(name) {
- this.name = name;
- }
- Animal.prototype.speak = function() {
- console.log(this.name + ' makes a noise.');
- };
- function Dog(name) {
- Animal.call(this, name);
- }
- util.inherits(Dog, Animal);
- Dog.prototype.speak = function() {
- console.log(this.name + ' barks.');
- };
- const d = new Dog('Rex');
- d.speak(); // Rex barks.
复制代码
需要注意,在 ES6 及之后的代码中,官方建议直接使用 class 与 extends 关键字替代 util.inherits,后者主要出现在需要维护的老代码里。
七、废弃标记:util.deprecate
util.deprecate 包装一个函数,使其被调用时发出弃用警告,适合在库升级时平滑提示调用方:
- const util = require('util');
- const oldFunction = util.deprecate(
- () => { console.log('This function is deprecated'); },
- 'oldFunction is deprecated. Use newFunction instead.'
- );
- oldFunction();
- // 输出: This function is deprecated
- // 同时 Node.js 会显示弃用警告
复制代码
八、自定义对象的 inspect 输出
在类中实现 [util.inspect.custom] 方法,可以改变 util.inspect 对该类实例的输出内容,便于调试时只看关键字段:
- const util = require('util');
- class CustomObject {
- constructor(value) {
- this.value = value;
- }
- [util.inspect.custom](depth, options) {
- return `CustomObject: ${this.value}`;
- }
- }
- const obj = new CustomObject('test');
- console.log(util.inspect(obj)); // CustomObject: test
复制代码
九、常用方法及引入版本
- util.callbackify(original):Promise 转回调风格函数,8.2.0
- util.debuglog(section):条件调试日志,0.11.3
- util.deprecate(fn, msg[, code]):标记废弃函数,0.8.0
- util.format(format[, ...args]):格式化字符串,0.5.3
- util.formatWithOptions(inspectOptions, format[, ...args]):带选项的格式化,10.0.0
- util.getSystemErrorName(err):获取系统错误名称,9.7.0
- util.inherits(constructor, superConstructor):原型继承,0.3.0
- util.inspect(object[, options]):对象字符串表示,0.3.0
- util.isDeepStrictEqual(val1, val2):深度严格相等比较,9.0.0
- util.promisify(original):回调转 Promise,8.0.0
- util.stripVTControlCharacters(str):剥离 ANSI 转义码,16.11.0
- util.parseArgs([config]):命令行参数解析,18.3.0
- util.types:类型检查工具对象,10.0.0
对照版本号可以在维护多版本 Node.js 项目时快速判断某个 API 是否可用,例如需要兼容 8.0.0 以下环境时就不能直接依赖 promisify。
十、使用注意事项
- 性能:util.inspect 处理大型对象或无限深度时有明显开销,生产环境日志中应谨慎使用,避免在高频路径上反复序列化。
- API 稳定性:部分 util 方法在文档中仍被标记为实验性功能,升级 Node.js 大版本前应确认状态。
- 现代替代:util.inherits 可用 class/extends 替代,新代码不建议继续引入。
- 错误处理:使用 promisify 后必须正确处理 Promise 拒绝,否则容易产生未捕获的 Promise rejection;callbackify 场景下则要检查回调首个 err 参数。
十一、小结
util 模块覆盖了类型判断(util.types)、格式化与调试(util.format、util.inspect)、回调与 Promise 互转(util.promisify、util.callbackify)、继承(util.inherits)和废弃标记(util.deprecate)等场景。其中 promisify 是日常开发中使用频率最高的方法,它能把大量基于回调的 Node.js 核心模块函数转换为返回 Promise 的函数,与 async/await 配合顺畅;而 inspect 的自定义输出能力则能让复杂对象的调试信息更可控。 |