食用源碼: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. 以設置壞境變量的方法來傳參

經過閱讀優秀源碼提升本身,厚積薄發。歡迎大神指正~

相關文章
相關標籤/搜索