库的介绍
一个微小的 JavaScript 调试工具, 以 Node.js 核心的调试技术为模型. 适用于 Node.js 和 web 浏览器 传送门 https://github.com/visionmedia/debug
知识点
判断是 node 坏境还是 浏览器 坏境.
- src/index.js
- if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
- module.exports = require('./browser.js');
- } else {
- module.exports = require('./node.js');
- }
复制代码
process.type === 'renderer' 判断是否为 electron 坏境.
webpack 定义了 process.browser 在浏览器中返回 true , 在 node 中它返回 false (webpack 是基于 document 判断是否为浏览器坏境)
process.__nwjs 判断是否为 nwjs 坏境.(类似 electron)官方传送门 https://nwjs.io/
判断终端是否支持颜色
通过第三方库 supports-color 判断终端是否支持颜色.
- src/node.js
- exports.colors = [ 6, 2, 3, 4, 5, 1 ];
- try {
- var supportsColor = require('supports-color');
- if (supportsColor && (supportsColor.stderr || supportsColor).level>= 2) {
- exports.colors = [
- 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68,
- 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134,
- 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
- 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204,
- 205, 206, 207, 208, 209, 214, 215, 220, 221
- ];
- }
- } catch (err) {
- // swallow - we only care if `supports-color` is available; it doesn't have to be.
- }
复制代码
以设置坏境变量的方法来传参
debug 允许用户可以使用如下格式定义 options (相当于给程序传参)
$ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
复制代码
相比于常见的给程序传参的格式 node script --xx=xx, 更酷一些.
- exports.inspectOpts = Object.keys(process.env).filter(function (key) {
- return /^debug_/i.test(key);
- }).reduce(function (obj, key) {
- var prop = key
- .substring(6)
- .toLowerCase()
- .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() });
- var val = process.env[key];
- if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
- else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
- else if (val === 'null') val = null;
- else val = Number(val);
- obj[prop] = val;
- return obj;
- }, {});
复制代码
通过以上格式设置的参数会存在 process.env 内. process.env 为一个对象, 以 DEBUG_COLORS=no 为例, DEBUG_COLORS 为 key,no 为 value.
例如:
- DEBUG_COLORS=no DEBUG_DEPTH=10
- process.env = {
- DEBUG_COLORS: no,
- DEBUG_DEPTH: 10
- }
复制代码
使用 reduce 将数组转换为对象
通过 reduce 把数组转换为对象, 技巧是 reduce 的第二个参数(可选), 初始化的值.
- var list = [1,2,3,4];
- var a = list.reduce((obj,num)=>{
- // 此时 obj 在第一次执行时就是初始化的值 {}
- obj[`${num}`] = num;
- return obj;
- },{})
- console.log(a);
- //a : { '1': 1, '2': 2, '3': 3, '4': 4 }
复制代码
正则处理多值判定
- if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
- else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
- else if (val === 'null') val = null;
- else val = Number(val);
复制代码
通过正则处理相同含义的字符串, 比起用 === 判定优雅许多.
- //bad
- if(val === 'yes' || val === 'on' || val === 'true'){
- val = true;
- }
- //good
- if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
复制代码
公共逻辑提取
src/common.js 为一个公共的模块, 导出值为函数 createDebug. 不管是 node 还是 浏览器 坏境最后都作为参数传入到 common 内, 这样就做到了代码分离, 把可重用的代码组织在一起.
两个对象之间的赋值
这里的 env 为坏境对象, 把 env 所有的属性挂载到 createDebug 对象. 这样可以随意扩展 env 对象, 最终所有方法都会赋值给导出的对象 createDebug.
- src/common.js
- // 赋值操作 源码
- Object.keys(env).forEach(function(key) {
- createDebug[key] = env[key];
- });
- // 便捷写法
- let obj1 = {'name':'cxr','age':18};
- let obj2 = {...obj1};
- console.log(obj2);//{ name: 'cxr', age: 18 }
- console.log(obj1 === obj2);//false
复制代码
检测 node 运行坏境是否为终端
利用 tty 模块 (nodejs 原生模块), 当 Node.js 检测到正运行在一个文本终端(TTY) 时, 则 process.stdin 默认会被初始化为 tty.ReadStream 实例, 且 process.stdout 和 process.stderr 默认会被初始化为 tty.WriteStream 实例. 通过 isTTY 来检测: 如果是 tty.WriteStream 实例, 则返回 true.
- // writeStream.isTTY 总是返回 true
- if(process.stdout.isTTY === true)
复制代码
文件描述符(fd)
首先要了解的是, 在 Linux 下, 一切皆为文件. 内核 (kernel) 利用文件描述符 (file descriptor) 来访问文件. 文件描述符是非负整数. 打开现存文件或新建文件时, 内核会返回一个文件描述符. 读写文件也需要使用文件描述符来指定待读写的文件. Linux 下:
0 是标准输入的文件描述符 -> stdin.
1 是标准输出的文件描述符 -> stdout.
2 是标准错误输出的文件描述符 -> stderr.
详细描述传送门 https://baike.baidu.com/item/文件描述符/9809582
这里通过 process.stderr.fd 返回一个 fd 作为参数, tty.isatty 检测如果给定的 fd 有关联 TTY, 则返回 true, 否则返回 false. 换句话说, 就是这个输出流是不是在终端里.
- // 用户如果不设置 color 选项的情况下 将检测错误流是否在终端里
- function useColors() {
- return 'colors' in exports.inspectOpts
- ? Boolean(exports.inspectOpts.colors)
- : tty.isatty(process.stderr.fd);
- }
复制代码
ANSI escape codes
用于控制视频文本终端上的光标位置, 颜色和其他选项. 某些字节序列 (大多数以 Esc 和'['开头) 嵌入到文本中, 终端查找并解释为命令, 而不是字符代码. 实现 node 坏境下带颜色输出的核心. https://en.wikipedia.org/wiki/ANSI_escape_code
node 中书写格式:
\u001b[31mHello 它将被解析为输出红色的 Hello .
复制代码
在 node 中使用改变终端字体颜色:
- //31m 为红色 32m 为绿色
- console.log('\u001b[31mHello \u001b[32mWorld');
复制代码
自定义格式化功能
debug 允许用户自定义格式化插件, 例如:
- const createDebug = require('debug')
- createDebug.formatters.h = (v) => {
- return v.toString('hex')
- }
- // ...elsewhere
- const debug = createDebug('foo')
- debug('this is hex: %h', new Buffer('hello world'))
- // foo this is hex: 68656c6c6f20776f726c6421 +0ms
复制代码
可以看到只需要在 createDebug.formatters 对象上挂载自定义的函数 h 即可. 在格式化输出的时候发现是 %h 的时候将调用用户自定义函数. 源码实现:
- // apply any `formatters` transformations
- var index = 0;
- args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
- // if we encounter an escaped % then don't increase the array index
- if (match === '%%') return match;
- index++;
- var formatter = createDebug.formatters[format];
- if ('function' === typeof formatter) {
- var val = args[index];
- match = formatter.call(self, val);
- // now we need to remove `args[index]` since it's inlined in the `format`
- args.splice(index, 1);
- index--;
- }
- return match;
- });
- // apply env-specific formatting (colors, etc.)
- createDebug.formatArgs.call(self, args);
复制代码
取出 % 后面的字符
基于字符匹配调用定义在 createDebug.formatters 上的函数
基于函数返回值, 替换
遵循了对修改关闭, 对扩展开放的原则(OCP).
总结
最后在总结下学习到的知识:
node 和 浏览器 坏境的区分.
重复代码提取 --DRY
对修改关闭, 对扩展开放 --OCP
ANSI escape codes
以设置坏境变量的方法来传参
通过阅读优秀源码提高自己, 厚积薄发. 欢迎大神指正~
来源: https://juejin.im/post/5b6d133ff265da0fa21ab0cb