Start with type judgment

Checking a variable’s type can be a headache in JavaScript, and can cause all sorts of problems if you simply use Typeof.

To name a few simple 🌰 :

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

Later, we found that you can use the Object. The prototype. The toString () method to the variable type of judgment.

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

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

Proxiing the toString() method gives us a string of type on which we can do things.

const getTypeString = obj= > {
  return Object.prototype.toString.call(obj)
}
const isType = type= > {
  return obj= > {
    return getTypeString(obj) === `[object ${type}] `}}const isArray = isType('Array') // This method is usually replaced by array.isarray

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

But, in Node.js, there is actually a set of internal apis for determining variable types. Moreover, the function is 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
Copy the code

Strictly equal

In JavaScript, in the process of determining the equality of objects, arrays and other variables, the use of === usually only determines whether the two variables refer to the same memory address. If you want to determine whether all values of an object’s keys are equal, you need to traverse both objects. Util also provides a method 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
Copy the code

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
Copy the code

Error First & Promise

Early Node apis were error-First style, which means that all asynchronous functions accept a callback that takes an Error object as an argument. If the Error object is normally returned as null, the subsequent argument is the result of a successful response.

// Here is an example of reading a file
const fs = require('fs')
fs.readFile('nginx.log'.(error, data) = > {
  if (error) {
    // Failed to read the file
    console.error(error)
    return
  }
  // Read the file successfully, print the result
  console.log(data)
})
Copy the code

With the release of Node 8, a promisify interface has been added to convert Error First style apis into Promise apis.

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))
Copy the code

Later, however, many felt that the way these native apis supported Promise was too cumbersome, and that each API required a separate layer of wrapping the Promisify method. At the time of The Release of Node 10, the native modules added a.Promises property, and all apis under this property will be 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))
Copy the code

Note: After Node 14, Promises API has added a new way to introduce promises by changing 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))
Copy the code

In addition to converting Error First style apis to Promise apis, util also provides callbackify methods for converting async functions to Error First style functions.

Let’s restore the promise-enabled FS to an Error First style function using Callbackify.

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)
})
Copy the code

Debugging and output

If you’ve ever developed a Node service, you’ve probably used the Debug module, which allows you to see more explicit debugging information on the console.

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

const user = { name: 'shenfq' }

log('Current user: %o', user)
Copy the code

A similar effect can be achieved with util.debug:

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

const user = { name: 'shenfq' }

log('Current user: %o', user)
Copy the code

Only at startup, you need to replace the DEBUG environment variable with NODE_DEBUG.

If you take a close look at the code above, you will notice that in the string before the log(‘ current user: % O ‘, user) method, there is a % O placeholder indicating that an object will be populated in this place. This is similar to printf in C or Python. Similarly, in the util module, the format method is provided directly: util.format.

const { format } = require('util')

console.log(
  format('Current user: %o', {
    name: 'shenfq'.age: 25}))Copy the code

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

A placeholder type
%s string
%d Numbers (both integer and floating point)
%i The integer
%f Floating point Numbers
%j JSON
%o Object

An object in JavaScript is a complicated thing. In addition to formatting an object directly using util.format plus a % O placeholder, util provides a method called inspect to format an object.

const { inspect } = require('util')

const user = {
  age: 25.name: 'shenfq'.work: {
    name: 'coding'.seniority: 5}}console.log(inspect(user))
Copy the code

It looks like inspect doesn’t do anything, but the inspect method has a second argument that does some personalization of the formatting.

  • depth: number: Control display hierarchy;
  • sorted: boolean|Function: Whether to sort by key encoding value;
  • compact: boolean: Whether to display in a single line;

Of course, the above is only a part of the configuration, more detailed configuration can be found in the Node documentation, let’s write a few examples:

All attributes are wrapped:

inspect(user, {
	compact: false
})
Copy the code

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

inspect(user, {
  depth: 0.compact: false
})
Copy the code

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

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