Nodejs开发进阶K-Util工具类

Nodejs中,有一个系统模块,名为Util。笔者理解,这就是一个工具类,有时也称为Utility或者Helper,是一些常用的,方便使用的,帮助开发的功能和方法集合。

由于它的分类,本来就比较杂,很多内容在普通的开发过程中可能用到的机会也不是特别大,这里笔者尝试对其进行分类,并列举几个比较常用或者特别的项目。

类和定义

util是一个nodejs的内置类,它的完整类定义如下:

shell 复制代码
[root@john-eosdev xdata3]# node
Welcome to Node.js v21.7.2.
Type ".help" for more information.
> util
{
  _errnoException: [Function: _errnoException],
  _exceptionWithHostPort: [Function: _exceptionWithHostPort],
  _extend: [Function: _extend],
  callbackify: [Function: callbackify],
  debug: [Function: debuglog],
  debuglog: [Function: debuglog],
  deprecate: [Function: deprecate],
  format: [Function: format],
  styleText: [Function: styleText],
  formatWithOptions: [Function: formatWithOptions],
  getSystemErrorMap: [Function: getSystemErrorMap],
  getSystemErrorName: [Function: getSystemErrorName],
  inherits: [Function: inherits],
  inspect: [Function: inspect] {
    custom: Symbol(nodejs.util.inspect.custom),
    colors: [Object: null prototype] {
      reset: [Array],
      bold: [Array],
      dim: [Array],
      italic: [Array],
      underline: [Array],
      blink: [Array],
      inverse: [Array],
      hidden: [Array],
      strikethrough: [Array],
      doubleunderline: [Array],
      black: [Array],
      red: [Array],
      green: [Array],
      yellow: [Array],
      blue: [Array],
      magenta: [Array],
      cyan: [Array],
      white: [Array],
      bgBlack: [Array],
      bgRed: [Array],
      bgGreen: [Array],
      bgYellow: [Array],
      bgBlue: [Array],
      bgMagenta: [Array],
      bgCyan: [Array],
      bgWhite: [Array],
      framed: [Array],
      overlined: [Array],
      gray: [Array],
      redBright: [Array],
      greenBright: [Array],
      yellowBright: [Array],
      blueBright: [Array],
      magentaBright: [Array],
      cyanBright: [Array],
      whiteBright: [Array],
      bgGray: [Array],
      bgRedBright: [Array],
      bgGreenBright: [Array],
      bgYellowBright: [Array],
      bgBlueBright: [Array],
      bgMagentaBright: [Array],
      bgCyanBright: [Array],
      bgWhiteBright: [Array]
    },
    styles: [Object: null prototype] {
      special: 'cyan',
      number: 'yellow',
      bigint: 'yellow',
      boolean: 'yellow',
      undefined: 'grey',
      null: 'bold',
      string: 'green',
      symbol: 'green',
      date: 'magenta',
      regexp: 'red',
      module: 'underline'
    },
    replDefaults: [Getter/Setter]
  },
  isArray: [Function: isArray],
  isBoolean: [Function: isBoolean],
  isBuffer: [Function: isBuffer],
  isDeepStrictEqual: [Function: isDeepStrictEqual],
  isNull: [Function: isNull],
  isNullOrUndefined: [Function: isNullOrUndefined],
  isNumber: [Function: isNumber],
  isString: [Function: isString],
  isSymbol: [Function: isSymbol],
  isUndefined: [Function: isUndefined],
  isRegExp: [Function: isRegExp],
  isObject: [Function: isObject],
  isDate: [Function: isDate],
  isError: [Function: isError],
  isFunction: [Function: isFunction],
  isPrimitive: [Function: isPrimitive],
  log: [Function: log],
  promisify: [Function: promisify] { custom: Symbol(nodejs.util.promisify.custom) },
  stripVTControlCharacters: [Function: stripVTControlCharacters],
  toUSVString: [Function: toUSVString],
  transferableAbortSignal: [Getter],
  transferableAbortController: [Getter],
  aborted: [Getter],
  types: {
    isExternal: [Function: isExternal],
    isDate: [Function: isDate],
    isArgumentsObject: [Function: isArgumentsObject],
    isBigIntObject: [Function: isBigIntObject],
    isBooleanObject: [Function: isBooleanObject],
    isNumberObject: [Function: isNumberObject],
    isStringObject: [Function: isStringObject],
    isSymbolObject: [Function: isSymbolObject],
    isNativeError: [Function: isNativeError],
    isRegExp: [Function: isRegExp],
    isAsyncFunction: [Function: isAsyncFunction],
    isGeneratorFunction: [Function: isGeneratorFunction],
    isGeneratorObject: [Function: isGeneratorObject],
    isPromise: [Function: isPromise],
    isMap: [Function: isMap],
    isSet: [Function: isSet],
    isMapIterator: [Function: isMapIterator],
    isSetIterator: [Function: isSetIterator],
    isWeakMap: [Function: isWeakMap],
    isWeakSet: [Function: isWeakSet],
    isArrayBuffer: [Function: isArrayBuffer],
    isDataView: [Function: isDataView],
    isSharedArrayBuffer: [Function: isSharedArrayBuffer],
    isProxy: [Function: isProxy],
    isModuleNamespaceObject: [Function: isModuleNamespaceObject],
    isAnyArrayBuffer: [Function: isAnyArrayBuffer],
    isBoxedPrimitive: [Function: isBoxedPrimitive],
    isArrayBufferView: [Function: isView],
    isTypedArray: [Function: isTypedArray],
    isUint8Array: [Function: isUint8Array],
    isUint8ClampedArray: [Function: isUint8ClampedArray],
    isUint16Array: [Function: isUint16Array],
    isUint32Array: [Function: isUint32Array],
    isInt8Array: [Function: isInt8Array],
    isInt16Array: [Function: isInt16Array],
    isInt32Array: [Function: isInt32Array],
    isFloat32Array: [Function: isFloat32Array],
    isFloat64Array: [Function: isFloat64Array],
    isBigInt64Array: [Function: isBigInt64Array],
    isBigUint64Array: [Function: isBigUint64Array],
    isKeyObject: [Function: value],
    isCryptoKey: [Function: value]
  },
  parseEnv: [Function: parseEnv],
  parseArgs: [Getter/Setter],
  TextDecoder: [Getter/Setter],
  TextEncoder: [Getter/Setter],
  MIMEType: [Getter/Setter],
  MIMEParams: [Getter/Setter]
}
>

虽然,util是一个nodejs的内置模块,但不是全局变量,需要直接引用,然后就可以使用了。

const util = require('node:util');

isDeepStrictEqual

这是一个比较有用的功能,用于判断两个对象的内容和值是完全相等的。如果没有这个方法,自己来实现,需要考虑到很多的因素,也不一定能够做得很好。

类型判断

使用util进行类型判断,是一个比较常见的需求和场景。从其类定义来看,可以直接进行的类型判断包括:

Array数组、Boolean布尔值、Buffer缓冲区、Null空、Undefined未定义、NullOrUndefined空或未定义、Number数字、String字符串、Symbol符号、RegExp正则、Object对象、Date日期、Error错误、Function方法、Primitive基础类型等等。

虽然用的比较少,但笔者看到,这些方法似乎比JS的typeof或者instanceof好像要细致和优雅,但它们并不是标准的JS方法,这样就在前端受到了一些限制,有机会希望可以尝试使用。

在更新版本的nodejs中,这一系列的类型判断函数已经被标记为"过期"。推荐的处理方式是使用util.types类的对应的判断方法,或者类型本身的判断方式,如Array.isArray,Buffer.isBuffer等等。除了上述的类型判断之外,它还提供了更细粒度的类型检查和判断,我们在其类定义里面也可以看到。读者有兴趣可以研读相关的技术文档。

Debug 调试

util.debuglog方法,用于创建一个调试方法,它可以基于环境变量的设置,条件化的输出调试内容到stderr接口。下面的示例代码方便我们进行理解:

js 复制代码
//  NODE_DEBUG = foo

const util = require('node:util');
const debuglog = util.debuglog('foo');

debuglog('hello from foo [%d]', 123); 

// 输出 
FOO 3245: hello from foo [123] 

这段代码说明:

  • 可以用debuglog方法,来创建一个日志方法
  • 参数是一个环境变量值
  • 当NODE_DEBUG环境变量设置为该值是,debuglog方法就可以执行了
  • 如果没有这个环境变量,则定义的debuglog不会实际执行,也不会输出内容
  • 如果有输出信息,则会包括环境变量和当前进程编号

还有一个util.debug方法,其实就是debuglog方法的别名。使用util.debug,开发者可以有条件的控制调试信息的输出。

Format 格式化

这个方法类似于C语言中的printf函数。它的输入包括一个模板字符串和一系列数值,输出是格式化后的字符串。format是同步方法,通常用作调试信息的显示和输出。

下面是其用法的简单的参考代码:

js 复制代码
util.format('%s:%s', 'foo');
// Returns: 'foo:%s' 

util.format('%s:%s', 'foo', 'bar', 'baz');
// Returns: 'foo:bar baz'

这里我们可以看到一些要点:

  • format方法的第一个参数,通常是带有类型变量占位符的字符串
  • format后续参数,将会按照类型定义,替换格式字符串中的占位符

format格式模板可选项目包括:

  • %s 字符串
  • %d 数字
  • %i 整数
  • %f 浮点数
  • %j JSON
  • %o 对象
  • %c CSS
  • %o 对象
  • %% 百分号转义

util还有一个相关的formatWithOptions方法,可以通过提供更多的选项,来丰富模板字符串的操作。

Inspect 检查器

我们在开发和调试的过程中,经常遇到需要打印对象内容的场景。默认的console.log方法可能不能够满足我们的需求,因为它可能会将内部的对象打印称为"[object]"这种方式,或者,它会忽略输出一些内容,这显然不是我们需要的。

这时,我们可以将console.log和util.inspect方法结合起来使用,它可以用于输出完整的JS对象的内容。下面是一个简单的示例:

js 复制代码
const { inspect } = require('node:util');

const obj = {};
obj.a = [obj];
obj.b = {};
obj.b.inner = obj.b;
obj.b.obj = obj;

console.log(inspect(obj));
// <ref *1> {
//   a: [ [Circular *1] ],
//   b: <ref *2> { inner: [Circular *2], obj: [Circular *1] }
// } 

这只是一个非常简单的示例和场景。实际上util的inspect方法功能非常强大,有很多额外的选项,比如可以自定义输出(自定义inspect方法),配置输出内容的格式包括颜色,选择是否显示非可枚举对象,显示深度,控制显示内容的大型长度,排序等等,有兴趣的读者可以自行查阅相关技术文档,获取更详细的信息。

MIMEType

在当前的版本中,这是一个实验性的特性。MIMEType就是对mime类型的支持。下面的例子让我们能够很容易的理解这一点:

js 复制代码
const { MIMEType } = require('node:util');

const myMIME = new MIMEType('text/javascript');
console.log(myMIME.type);
// Prints: text
myMIME.type = 'application';
console.log(myMIME.type);
// Prints: application
console.log(String(myMIME));
// Prints: application/javascript

有趣的是,不知道为什么nodejs社区会将这个功能设计在util模块中,笔者个人觉得,将mimetype设计到http模块中,或者作为一个独立的模块,可能更会更合适一点吧。

parseArgs/parseEnv 解析参数和环境变量

parseArgs可以用于解析process.argv中的内容,可以通过选项参数来控制内容和转换的规则,最后输出一个参数对象。如下面的示例:

js 复制代码
const { parseArgs } = require('node:util');
const args = ['-f', '--bar', 'b'];
const options = {
  foo: {
    type: 'boolean',
    short: 'f',
  },
  bar: {
    type: 'string',
  },
};
const {
  values,
  positionals,
} = parseArgs({ args, options });
console.log(values, positionals);
// Prints: [Object: null prototype] { foo: true, bar: 'b' } []

和parseArg的诉求和原理类似,parseEnv可以将.env文件中的内容,转换为一个对象。

Promisify Promise化

此方法可以将一个异步回调函数,转换称为一个Promise对象,然后使用Promise的方法来执行。比如下面这个简单的示例:

js 复制代码
const util = require('node:util');
const fs = require('node:fs');

const stat = util.promisify(fs.stat);

stat('.').then((stats) => {
  // Do something with `stats`
}).catch((error) => {
  // Handle the error.
}); 

// async/await 调用方式
async function callStat() {
  const stats = await stat('.');
  console.log(`This directory is owned by ${stats.uid}`);
}

callStat(); 

直接使用util.promisify,来对某个异步回调函数进行promise化,其实有一个条件,就是它的callback方法的参数,必须是(error,data)这种形式的,如果不是这种类型,可能需要使用util.promisify.custom来完全自定义方法的promise化,这种情况称为Custom Promisified Functions,自定义Promise函数。

Encoder/Decoder 文本编解码器

这个没有什么特别的,应该就是WHATWH Encoding标准的 text Encoder/Decoder API的实现。和全局变量TexeEncoder/Decoder好像没什么差异。

Log 日志

可以向标准stdout输出带有时间戳的调试信息。

js 复制代码
> util.log('Timestamped message.');
18 Apr 11:32:29 - Timestamped message.

deprecate 弃用

我们在开发工作中,经常会遇到某个功能版本被标记成为"弃用"的情况。这是一种在软件开发行业在系统演进过程中,尽量保证这个过程平稳过渡的管理和控制机制。它的基础原理是,通过一种一致的约定和规范,可以选择将一个功能、API或者函数标记成为"弃用",这时并不意味着它会被立刻删除或者停用,但它会在被使用时开发者和用户都会收到一些提示信息;这样在一个过渡周期当中,开发者就可以优先选择使用新的特性或者替代技术方案;最终经过一段时间的发展,当源头开发者看到老版本的使用降低到某个程度之后,就可以真正考虑将老特性从系统中移除了。

关于弃用机制,开发者应该理解以下要点:

  • 弃用(deprecated)不等于立即删除

不用过于惊慌,它依然可以在当前甚至相当长的一段时间内可用,但应当在新系统或者更新时考虑改进和替代的技术方案。

  • 向后兼容性

弃用通常是为了保持向后兼容性,给开发人员一个过渡期去适应变更,并修改代码来适应这个弃用,这样可以防止意外破坏现有系统。

  • 删除计划和版本

弃用是对未来移除该功能特性的一个预告和警告。它向开发人员发出信号,某些遗留功能在未来版本中计划被删除。一个良好的弃用方案还可能包括弃用的版本规划,即表明在那些版本可能实施真正的移除操作。

  • 替代方案

作为原始代码的开发者,如果考虑将特性标记成为"弃用"时,应当提供相关的升级或替代方案。

  • 编译器/IDE警告

作为一致的约定,大多数编译器和IDE会显示警告信息提醒开发者,正在使用的代码元素已被标记为deprecated,需要注意更新。此外一些弃用信息也会出现在程序启动、调试或者运行过程中,对开发者和用户发出提示。

nodejs的util模块,就提供了弃用信息注入的方法。我们可以通过示例代码来简单了解一下:

js 复制代码
const util = require('node:util');

const fn1 = util.deprecate(someFunction, someMessage, 'DEP0001');
const fn2 = util.deprecate(someOtherFunction, someOtherMessage, 'DEP0001');
fn1(); // Emits a deprecation warning with code DEP0001
fn2(); // Does not emit a deprecation warning because it has the same code 

这些弃用信息在开发的过程中,可能确实对于开发者是有用的,但在生产环境中,可能会带来一些困扰。这时可以通过一些启动选择,可选不显示这些信息。如 --no-deprecation 或者 --no-warnings等。

其他杂项

util的其他杂项包括:

  • getSystemErrorName(err) 获取错误的系统名称
  • getSystemErrorMap() 获取相关所有系统错误列表
  • callBackify(original) 将一个同步调用方法,转换称为异步回调的模式
  • styleText() 将文本风格化
  • stripVTControlCharacters(str) 剥离逃逸码
  • transferableAbortController/transferableAbortSignal 撤销控制相关
相关推荐
㳺三才人子5 小时前
初探 Flask
后端·python·flask·html
星栈独行5 小时前
我在 Rust 全栈项目里用 JWT 做无状态认证
开发语言·后端·rust·前端框架·开源·github·web
Java爱好狂.6 小时前
Java程序员体系化学习路线(2026最新版)
java·后端·java面试·java架构师·java程序员·java八股文·java学习路线
陈随易6 小时前
Redis 8.8发布,一定要更新
前端·后端·程序员
装不满的克莱因瓶6 小时前
SpringBoot 如何将 lib 目录中jar包打包进最终的jar包里面
spring boot·后端·maven·jar·mvn
晓说前端7 小时前
第一篇:为什么学TypeScript?—— 优势、场景与环境搭建
javascript·ubuntu·typescript
ltl7 小时前
Transformer 原论文实验结果:为什么 28.4 BLEU 足以改写路线图
后端
excel8 小时前
为什么我推荐使用 Termius:现代 SSH 工具的完整体验
前端·后端
ZC跨境爬虫8 小时前
模块化烹饪小程序开发日记 Day7:(菜谱详情接口开发与JSON数据读取全流程)
前端·javascript·css·ui·微信小程序·json
এ慕ོ冬℘゜8 小时前
JS 前端基础面试题
开发语言·前端·javascript