Node.js Util you didn't know

Shenfq
中文

Speaking from the type judgment

In JavaScript, the type verification of variables is a very headache. If you simply use typeof , you will encounter various problems.

Give a few simple 🌰:

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

Later, everyone found that the Object.prototype.toString() method can be used to judge the variable type.

const getTypeString = obj => Object.prototype.toString.call(obj)

getTypeString(null) // '[object Null]'
getTypeString('string') //'[object String]'
getTypeString(new String) //'[object String]'

By toString() method, we can get a type string, and we can do things on this string.

const getTypeString = obj => {
  return Object.prototype.toString.call(obj)
}
const isType = type => {
  return obj => {
    return getTypeString(obj) === `[object ${type}]`
  }
}

const isArray = isType('Array') // 该方法一般通过 Array.isArray 代替

const isNull = isType('Null')
const isObject = isType('Object')
const isRegExp = isType('RegExp')
const isFunction = isType('Function')
const isAsyncFunction = isType('AsyncFunction')
isNull(null) // true
isObject({}) // true
isRegExp(/\w/) // true
isFunction(() => {}) // true
isAsyncFunction(async () => {}) // true

But, in Node.js, there is actually a set of apis used to determine the type of variables. Moreover, the functions are extremely rich. In addition to the judgment of basic types, it also supports the judgment of Promise objects, Date objects, and various ArrayBuffers.

const types = require('util/types')

types.isDate(new Date) // true
types.isPromise(new Promise(() => {})) // true
types.isArrayBuffer(new ArrayBuffer(16)) // true

Strictly equal

In JavaScript, in the process of judging the equality of variables such as objects and arrays, if === is used, it usually only judges whether these two variables point to the same memory address. If you want to determine whether all the values corresponding to the keys of an object are equal, you need to traverse the two objects. In util , a method is also provided to determine whether two objects are strictly equal: util.isDeepStrictEqual(val1, val2)

const util = require('util')

const val1 = { name: 'shenfq' }
const val2 = { name: 'shenfq' }

console.log('val1 === val2', val1 === val2) // false
console.log('isDeepStrictEqual', util.isDeepStrictEqual(val1, val2)) // true

This method can also be used to determine whether an array is strictly equal:

const util = require('util')

const arr1 = [1, 3, 5]
const arr2 = [1, 3, 5]

console.log('arr1 === arr2', arr1 === arr2) // false
console.log('isDeepStrictEqual', util.isDeepStrictEqual(arr1, arr2)) // true

Error First & Promise

The early Node APIs are all in the Error First , that is, all asynchronous functions will accept a callback function. One parameter of the callback is the error object. If the error object is null , 061931ec2cc57b, the latter parameter is the result of a successful response.

// 下面是一个读取文件的示例
const fs = require('fs')
fs.readFile('nginx.log', (error, data) => {
  if (error) {
    // 读取文件失败
    console.error(error)
    return
  }
  // 读取文件成功,打印结果
  console.log(data)
})

When Node 8 was released, a new promisify interface was added to convert the Error First style API to the Promise API.

const fs = require('fs')
const util = require('util')

const readFile = util.promisify(fs.readFile)
readFile('./2021-11-11.log', { encoding: 'utf-8' })
  .then(text => console.log(text)) 
    .catch(error => console.error(error))

However, many people later felt that the way these native APIs support Promise was too cumbersome, and each API required a separate layer of the promisify method. When Node 10 was released, the native module added a .promises attribute, and all APIs under this attribute were Promise-style.

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

Note that : After Node 14, a promises API, which is introduced by modifying the package name.

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

In addition to converting the Error First style API to the Promise API, util also provides the callbackify method to convert the async function to the Error First style function.

Next, use callbackify to fs to the Error First style function.

const fs = require('fs/promises')
const util = require('util')

const readFile = util.callbackify(fs.readFile)
readFile('./2021-11-12.log', { encoding: 'utf-8' }, (error, text) => {
  if (error) {
    console.error(error)
    return
  }
  console.log(text)
})

Debug and output

If you have developed a Node service, you should have used the debug module. Through this module, you can see more clear debugging information on the console.

const debug = require('debug')
const log = debug('app')

const user = { name: 'shenfq' }

log('当前用户: %o', user)

In fact, a similar effect can be achieved util.debug

const debug = require('debug')
const log = debug('app')

const user = { name: 'shenfq' }

log('当前用户: %o', user)

Just at startup, you need to DEBUG environment variable with NODE_DEBUG .

If you look at the above code carefully, you should find that in the before the 1619331ec2ccc1e log('current user: %o', user) method, there is a %o placeholder, indicating that this place will be filled with an object ( object). This is similar to printf in C language or python. Similarly, in the util module, the formatting method is directly provided: util.format .

const { format } = require('util')

console.log(
  format('当前用户: %o', {
    name: 'shenfq', age: 25
  })
)

In addition to the %o placeholder, different data types should use different placeholders.

Placeholdertype
%sString
%dNumbers (including integers and floating point numbers)
%iInteger
%fFloating point
%jJSON
%oObject

Objects in JavaScript are a very complicated thing. In addition to formatting objects util.format plus %o util also provides a inspect to format objects.

const { inspect } = require('util')

const user = {
  age: 25,
  name: 'shenfq',
  work: {
    name: 'coding',
    seniority: 5
  }
}

console.log(inspect(user))

Looking at inspect seems to have done nothing, but the inspect method also has a second parameter, which is used for some personalized configuration during formatting.

  • depth: number : Control the display level;
  • sorted: boolean|Function : Whether to sort according to the code value of the key;
  • compact: boolean : Whether to display a single line;

Of course, the above is only a part of the configuration. For more detailed configuration, please refer to the node documentation. Below we write a few cases:

All attributes are displayed in a new line:

inspect(user, {
    compact: false
})

Format only the value of the first level of the object:

inspect(user, {
  depth: 0,
    compact: false
})

Output in reverse order according to the encoding of the key value:

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

阅读 2.1k

自然醒的笔记本
学习过程中的一些总结和沉淀,欢迎关注公众号「自然醒的笔记本」
3.9k 声望
6.8k 粉丝
0 条评论
3.9k 声望
6.8k 粉丝
文章目录
宣传栏