食用源码:debug

库的介绍

一个微小的JavaScript调试工具,以Node.js核心的调试技术为模型。 适用于Node.js和Web浏览器 传送门node

知识点

判断是 node 坏境仍是 浏览器 坏境。

src/index.jswebpack

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)官方传送门

判断终端是否支持颜色

经过第三方库 supports-color 判断终端是否支持颜色。git

src/node.jsgithub

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 (至关于给程序传参)web

$ 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。浏览器

例如:bash

DEBUG_COLORS=no DEBUG_DEPTH=10
        process.env = {
            DEBUG_COLORS: no,
            DEBUG_DEPTH: 10
        }
复制代码

使用 reduce 将数组转换为对象

经过 reduce 把数组转换为对象,技巧是 reduce 的第二个参数(可选),初始化的值。app

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);
复制代码

经过正则处理相同含义的字符串,比起用 === 断定优雅许多。electron

//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。

详细描述传送门

这里经过 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 坏境下带颜色输出的核心wiki

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);
复制代码
  1. 取出%后面的字符
  2. 基于字符匹配调用定义在 createDebug.formatters 上的函数
  3. 基于函数返回值,替换

遵循了对修改关闭,对扩展开放的原则(OCP)。

总结

最后在总结下学习到的知识:

  1. node 和 浏览器 坏境的区分。
  2. 重复代码提取 --DRY
  3. 对修改关闭,对扩展开放 --OCP
  4. ANSI escape codes
  5. 以设置坏境变量的方法来传参

经过阅读优秀源码提升本身,厚积薄发。欢迎大神指正~

相关文章
相关标签/搜索