你不知道的 Node.js util

开发 前端
在 JavaScript 中,进行变量的类型校验是一个非常令人头疼的事,如果只是简单的使用 typeof 会到各种各样的问题。

[[435192]]

从类型判断说起

在 JavaScript 中,进行变量的类型校验是一个非常令人头疼的事,如果只是简单的使用 typeof 会到各种各样的问题。

举几个简单的🌰:

  1. console.log(typeof null) // 'object' 
  2. console.log(typeof new Array) // 'object' 
  3. console.log(typeof new String) // 'object' 

后来,大家发现可以使用 Object.prototype.toString() 方法来进行变量类型的判断。

  1. const getTypeString = obj => Object.prototype.toString.call(obj) 
  2.  
  3. getTypeString(null) // '[object Null]' 
  4. getTypeString('string') //'[object String]' 
  5. getTypeString(new String) //'[object String]' 

对 toString() 方法进行代理,可以得到一个类型字符串,我们就可以在这个字符串上面搞事情。

  1. const getTypeString = obj => { 
  2.   return Object.prototype.toString.call(obj) 
  3. const isType = type => { 
  4.   return obj => { 
  5.     return getTypeString(obj) === `[object ${type}]` 
  6.   } 
  7.  
  8. const isArray = isType('Array') // 该方法一般通过 Array.isArray 代替 
  9.  
  10. const isNull = isType('Null'
  11. const isObject = isType('Object'
  12. const isRegExp = isType('RegExp'
  13. const isFunction = isType('Function'
  14. const isAsyncFunction = isType('AsyncFunction'
  1. isNull(null) // true 
  2. isObject({}) // true 
  3. isRegExp(/\w/) // true 
  4. isFunction(() => {}) // true 
  5. isAsyncFunction(async () => {}) // true 

But,在 Node.js 中,内部其实是有一组用来判断变量类型的 api 的。而且功能异常丰富,除了基础类型的判断,还支持判断 Promise 对象、Date 对象、各种ArrayBuffer。

  1. const types = require('util/types'
  2.  
  3. types.isDate(new Date) // true 
  4. types.isPromise(new Promise(() => {})) // true 
  5. types.isArrayBuffer(new ArrayBuffer(16)) // true 

严格相等

在 JavaScript 中,对象、数组等变量在判断相等的过程中,如果用 === 通常只会判断这两个变量是否指向同一内存地址。如果想判断对象的键对应的所有值是否相等,需要对两个对象进行遍历。在 util 中,也提供了一个方法可以用来判断两个对象是否严格相等:util.isDeepStrictEqual(val1, val2)

  1. const util = require('util'
  2.  
  3. const val1 = { name'shenfq' } 
  4. const val2 = { name'shenfq' } 
  5.  
  6. console.log('val1 === val2', val1 === val2) // false 
  7. console.log('isDeepStrictEqual', util.isDeepStrictEqual(val1, val2)) // true 

该方法同样可以用来判断数组,是否严格相等:

  1. const util = require('util'
  2.  
  3. const arr1 = [1, 3, 5] 
  4. const arr2 = [1, 3, 5] 
  5.  
  6. console.log('arr1 === arr2', arr1 === arr2) // false 
  7. console.log('isDeepStrictEqual', util.isDeepStrictEqual(arr1, arr2)) // true 

Error First & Promise

早期的 Node API 都是 Error First 风格的,也就是所有的异步函数都会接受一个回调函数,该回调的一个参数为 error 对象,如果正常返回 error 对象为 null,后面的参数为成功响应的结果。

  1. // 下面是一个读取文件的示例 
  2. const fs = require('fs'
  3. fs.readFile('nginx.log', (error, data) => { 
  4.   if (error) { 
  5.     // 读取文件失败 
  6.     console.error(error) 
  7.     return 
  8.   } 
  9.   // 读取文件成功,打印结果 
  10.   console.log(data) 
  11. }) 

在 Node 8 发布的时候,新增了一个 promisify 接口,用于将 Error First 风格的 API 转为 Promise API。

  1. const fs = require('fs'
  2. const util = require('util'
  3.  
  4. const readFile = util.promisify(fs.readFile) 
  5. readFile('./2021-11-11.log', { encoding: 'utf-8' }) 
  6.   .then(text => console.log(text))  
  7.  .catch(error => console.error(error)) 

不过,后来也有很多人觉得这些原生 API 支持 Promise 的方式太过繁琐,每个 API 都需要单独的包装一层 promisify 方法。在 Node 10 发布的时候,原生模块都新增了一个 .promises 属性,该属性下的所有 API 都 Promise 风格的。

  1. const fs = require('fs').promises 
  2. fs.readFile('./2021-11-11.log', { encoding: 'utf-8' }) 
  3.   .then(text => console.log(text))  
  4.  .catch(error => console.error(error)) 

 

注意:Node 14 后,promises API 又新增了一种引入方式,通过修改包名的方式引入。

  1. const fs = require('fs/promises'
  2. fs.readFile('./2021-11-11.log', { encoding: 'utf-8' }) 
  3.   .then(text => console.log(text))  
  4.  .catch(error => console.error(error)) 

除了将 Error First 风格的 API 转为 Promise API,util 中还提供 callbackify 方法,用于将 async 函数转换为 Error First 风格的函数。

下面通过 callbackify 将 promise 化的 fs 还原为 Error First 风格的函数。

  1. const fs = require('fs/promises'
  2. const util = require('util'
  3.  
  4. const readFile = util.callbackify(fs.readFile) 
  5. readFile('./2021-11-12.log', { encoding: 'utf-8' }, (error, text) => { 
  6.   if (error) { 
  7.     console.error(error) 
  8.     return 
  9.   } 
  10.   console.log(text) 
  11. }) 

调试与输出

如果有开发过 Node 服务,应该都用过 debug 模块,通过该模块可以在控制台看到更加明晰的调试信息。

  1. const debug = require('debug'
  2. const log = debug('app'
  3.  
  4. const user = { name'shenfq' } 
  5.  
  6. log('当前用户: %o'user

其实,通过 util.debug 也能实现类似的效果:

  1. const debug = require('debug'
  2. const log = debug('app'
  3.  
  4. const user = { name'shenfq' } 
  5.  
  6. log('当前用户: %o'user

只是在启动时,需要将 DEBUG 环境变量替换为 NODE_DEBUG。

如果你有认真看上面的代码,应该会发现,在 log('当前用户: %o', user) 方法前面的字符串中,有一个 %o 占位符,表示这个地方将会填充一个对象(object)。这与 C 语言或 python 中的,printf 类似。同样,在 util 模块中,直接提供了格式化的方法:util.format。

  1. const { format } = require('util'
  2.  
  3. console.log( 
  4.   format('当前用户: %o', { 
  5.     name'shenfq', age: 25 
  6.   }) 

除了 %o 占位符,不同的数据类型应使用不同的占位符。

JavaScript 中的对象是一个很复杂的东西,除了直接使用 util.format 外加 %o 占位符的方式格式化对象,util 中还提供了一个叫做 inspect 方法来进行对象格式化。

  1. const { inspect } = require('util'
  2.  
  3. const user = { 
  4.   age: 25, 
  5.   name'shenfq'
  6.   work: { 
  7.     name'coding'
  8.     seniority: 5 
  9.   } 
  10.  
  11. console.log(inspect(user)) 

这么看 inspect 好像什么都没做,但是 inspect 方法还有第二个参数,用来进行格式化时的一些个性化配置。

  • depth: number:控制显示层级;
  • sorted: boolean|Function: 是否按照key的编码值进行排序;
  • compact: boolean:是否进行单行显示;

当然上面只是一部分配置,更详细的配置可查阅 node 文档,下面我们写几个案例:

所有的属性都换行显示:

  1. inspect(user, { 
  2.  compact: false 
  3. }) 

只格式化对象第一层的值:

  1. inspect(user, { 
  2.   depth: 0, 
  3.  compact: false 
  4. }) 

按照key值的编码倒序输出:

  1. inspect(user, { 
  2.  compact: false
  3.   sorted: (a, b) => a < b ? 1 : -1 
  4. }) 

 

 

责任编辑:姜华 来源: 自然醒的笔记本
相关推荐

2018-12-12 15:05:13

2021-12-29 11:38:59

JS前端沙箱

2024-02-05 11:55:41

Next.js开发URL

2020-06-12 09:20:33

前端Blob字符串

2020-07-28 08:26:34

WebSocket浏览器

2020-07-03 14:30:34

Node内存前端

2011-12-09 11:16:48

Node.js

2009-12-10 09:37:43

2021-02-01 23:23:39

FiddlerCharlesWeb

2011-09-15 17:10:41

2022-10-13 11:48:37

Web共享机制操作系统

2020-12-14 07:51:16

JS 技巧虚值

2010-08-23 09:56:09

Java性能监控

2024-04-26 09:03:31

Node.jsCurrent发布版

2010-07-12 10:03:50

ibmdwjava

2022-11-04 08:19:18

gRPC框架项目

2020-09-15 08:35:57

TypeScript JavaScript类型

2022-11-16 08:43:30

Node.js模块

2020-08-11 11:20:49

Linux命令使用技巧

2012-11-23 10:57:44

Shell
点赞
收藏

51CTO技术栈公众号