1. 程式人生 > >[譯] 你不知道的 console 命令

[譯] 你不知道的 console 命令

相比使用 console.log 去輸出值,我們有更多的方式去除錯 JavaScript。你以為我要聊偵錯程式麼?不不不你想錯了。

告訴寫 JavaScript 的人應該使用瀏覽器的偵錯程式去除錯程式碼,這看來很不錯,並且肯定有其適用的時間和場合。但是大多數時候你僅僅只想檢視一段特定的程式碼是否執行或者一個變數的值是什麼,而不是迷失在 RxJS 程式碼庫或者一個 Promise 庫的深處。

然而,儘管 console.log 有其適用的場合,大多數人仍然沒有意識到 console 本身除了基礎 log 還有許多選擇。合理使用這些方法能讓除錯更簡單、更快速,並且更加直觀。

console.log()

很多人不知道經典的 console.log 其實有著豐富的函式特性。儘管大多數人只使用 console.log(object) 這種語法,但你仍然能寫 console.log(object, otherObject, string) 並且它會將所有東西都整齊的打印出來。有時候確實很方便。

不止那些,這兒還有另一種格式:console.log(msg, values)。這個執行方式和 C 或者 PHP 的 sprintf

很相似。

console.log('I like %s but I do not like %s.', 'Skittles', 'pus');
複製程式碼

會準確的輸出你所預期的東西。

> I like Skittles but I do not like pus.
複製程式碼

一般的佔位符有 %o(這是字元 o,不是 0)表示一個物件,%s 表示一個字串,以及 %d 代表一個小數或者整數。

你可能並不認為另一個有趣是 %c。實際上它是作為 CSS 值的佔位符。

console.log('I am a %cbutton', 'color: white; background-color: orange; padding: 2px 5px; border-radius: 2px'
); 複製程式碼

後面的值可以一直新增,在這裡沒有「結束標籤」確實有點怪異。但是你可以像這樣將它們隔開。

這並不優美,也不是特別的有用。當然這也不是真實的按鈕。

真的很有用嗎?不太認同。

console.dir()

通常來看,console.dir() 功能和 log() 非常相似,儘管看起來有略微不同。

下拉小箭頭展示的物件資訊和 console.log 的視圖裡一樣。但是在你觀察元素節點的時候,兩者結果會非常有趣並且截然不同。

let element = document.getElementById('2x-container');
複製程式碼

這是 log 輸入 element 的輸出:

我打開了一些元素節點。清晰的展示了 DOM 節點,一覽無餘,而且我們還可以跳轉到子DOM節點。但是 console.dir(element) 給我們一個意外不同的輸出。

這是一種更物件化的方式去觀察元素節點。也許在某些像監測元素節點的時候,這樣的結果才是你所想要的。

console.warn()

可能是 log() 最直接明顯的替換,你可以用相同的方式使用 console.warn()。唯一的區別在於輸出是一抹黃色。確切的說,輸出是一個 warn 級別而不是一個 info 級別的資訊,因此瀏覽器的處理稍稍有些不同。在一堆雜亂的輸出中高亮你的輸出是很有效果的。

不過,這還有一個更大的優點。因為輸出是一個 warn 級別而不是一個 info 級別,你可以將所有的 console.log 過濾掉只留下 console.warn。這有時在那些不停輸出一堆無用和無意義的東西到瀏覽器的隨意的應用程式中是非常有用。遮蔽干擾能更容易的看到你自己的輸出。

console.table()

令人驚訝的是這個並沒有廣為人知,但是 console.table() 方法更偏向於一種方式展示列表形式的資料,這比只扔下原始的物件陣列要更加整潔。

舉一個例子,下面是資料的列表。

const transactions = [{
  id: "7cb1-e041b126-f3b8",
  seller: "WAL0412",
  buyer: "WAL3023",
  price: 203450,
  time: 1539688433
},
{
  id: "1d4c-31f8f14b-1571",
  seller: "WAL0452",
  buyer: "WAL3023",
  price: 348299,
  time: 1539688433
},
{
  id: "b12c-b3adf58f-809f",
  seller: "WAL0012",
  buyer: "WAL2025",
  price: 59240,
  time: 1539688433
}];
複製程式碼

如果使用 console.log 去列出以上資訊,我們能得到一些中看不中用的輸出:

▶ (3) [{…}, {…}, {…}]
複製程式碼

這小箭頭允許你點選並會展開這個陣列,但這並不是我們想要的「一目瞭然」。

console.table(data) 的輸出則對我們更為有幫助。

第二個可選引數是你想要顯示列表的某列。預設是整個列表,但是我們也能這樣做。

> console.table(data, ["id", "price"]);
複製程式碼

我們得到這樣的輸出,僅僅只展示 id 和 price。在有著大量不相關資訊的龐雜物件中非常有用。index 列是自動生成的並且據我所知是不會消失。

值得一提的是在最右一列頭部的右上角有個箭頭可以顛倒次序。點選了它,會排序整個列。非常方便的找出一列的最大或者最小值,或者只是得到不同的資料展示形式。這個功能特性並沒有做什麼,只是對列的展示。但總會是有用的。

console.table() 只有處理最多1000行的資料的能力,所以它可能並不適用於所有的資料集合。

console.assert()

一個經常被忽視的實用的函式,assert() 在第一個引數是 falsey 時和 log() 一樣。當第一個引數為真值時也什麼都不做。

這個在你需要迴圈(或者不同的函式呼叫)並且只有一個要顯示特殊的行為的場景下特別有用。本質上和這個是一樣的。

if (object.whatever === 'value') {
  console.log(object);
}
複製程式碼

澄清一下,當我說「一樣」的時候,我本應該說是做相反的事。所以你需要變換一下場合。

所以,假設我們上面的值在時間戳裡有一個 null 或者 0,這會破壞我們程式碼日期格式。

console.assert(tx.timestamp, tx);
複製程式碼

當和任何有效的事物物件一起使用時會跳過。但是有一個觸發了我們的日誌記錄,因為時間戳在 0 和 null 時為假值

有時我們想要更加複雜的場景。舉個例子,我們看到了關於使用者 WAL0412 的資料問題並且想要只展示來自它們的事務。這將會是一個非常簡便的方案。

console.assert(tx.buyer === 'WAL0412', tx);
複製程式碼

看起來正確,但是並不奏效。牢記,場景必須是為否定態,我門想要的是斷言,而不是過濾

console.assert(tx.buyer !== 'WAL0412', tx);
複製程式碼

我們想做的就是這樣。在那種情況下,所有不是 WAL0412 號顧客的事務都為真值,只留下那些符合的事務。或者,也不完全是。

諸如此類,console.assert() 並不是一直都很管用。但是在特定的場景下會是最優雅的的解決方法。

console.count()

另外一個合適的用法是,將console作為一個計數器使用。

for(let i = 0; i < 10000; i++) {
  if(i % 2) {
    console.count('odds');
  }
  if(!(i % 5)) {
    console.count('multiplesOfFive');
  }
  if(isPrime(i)) {
    console.count('prime');
  }
}
複製程式碼

這不是一段有用的程式碼,並且有點抽象。我也不打算去證明 isPrime 函式,只假設可以執行。

我們將得到應該是這樣的列表

odds: 1
odds: 2
prime: 1
odds: 3
multiplesOfFive: 1
prime: 2
odds: 4
prime: 3
odds: 5
multiplesOfFive: 2
...
複製程式碼

以及剩下的。在你只想列出索引,或者想保留一次(或多次)計數的情況下非常有用。

你也能像那樣使用 console.count(),不需要引數。使用 default 呼叫。

這還有關聯函式 console.countReset(),如果你希望重置計數器可以使用它。

console.trace()

這在簡單的資料中演示更加困難。在你試圖找出有問題的內部類或者庫的呼叫這一塊是它最擅長。

舉個例子,這兒可能有 12 個不同的元件正在呼叫一個服務,但是其中一個沒有正確配置依賴。

export default class CupcakeService {
    
  constructor(dataLib) {
    this.dataLib = dataLib;
    if(typeof dataLib !== 'object') {
      console.log(dataLib);
      console.trace();
    }
  }
  ...
}
複製程式碼

這裡單獨使用 console.log() 我們只能知道執行了哪一個基礎庫,並不知道執行的具體位置。但是,堆疊軌跡會清楚的告訴我們問題在於 Dashboard.js,我們從中發現 new CupcakeService(false) 是造成出錯的罪魁禍首。

console.time()

console.time() 是專門用於監測操作的時間開銷的函式,也是監測 JavaScript 細微時間的更好的方式。

function slowFunction(number) {
  var functionTimerStart = new Date().getTime();
  // something slow or complex with the numbers. 
  // Factorials, or whatever.
  var functionTime = new Date().getTime() - functionTimerStart;
  console.log(`Function time: ${ functionTime }`);
}
var start = new Date().getTime();

for (i = 0; i < 100000; ++i) {
  slowFunction(i);
}

var time = new Date().getTime() - start;
console.log(`Execution time: ${ time }`);
複製程式碼

這是一個過時的方法。我指的同樣還有上面的 console.log。大多數人沒有意識到這裡你本可以使用模版字串和插值法。它時不時的會幫助到你。

那麼讓我們更新一下上面的程式碼。

const slowFunction = number =>  {
  console.time('slowFunction');
  // something slow or complex with the numbers. 
  // Factorials, or whatever.
  console.timeEnd('slowFunction');
}
console.time();

for (i = 0; i < 100000; ++i) {
  slowFunction(i);
}
console.timeEnd();
複製程式碼

我現在不需要去做任何算術或者設定臨時變數。

console.group()

如今我們可能在大多數 console 中要輸出高階和複雜的東西。分組可以讓你歸納這些。尤其是讓你能使用巢狀。它擅長展示程式碼中存在的結構關係。

// this is the global scope
let number = 1;
console.group('OutsideLoop');
console.log(number);
console.group('Loop');
for (let i = 0; i < 5; i++) {
  number = i + number;
  console.log(number);
}
console.groupEnd();
console.log(number);
console.groupEnd();
console.log('All done now');
複製程式碼

這又有一點難以理解。你可以看看這裡的輸出。

這並不是很有用,但是你能看到其中一些是如何組合的。

class MyClass {
  constructor(dataAccess) {
    console.group('Constructor');
    console.log('Constructor executed');
    console.assert(typeof dataAccess === 'object', 
      'Potentially incorrect dataAccess object');
    this.initializeEvents();
    console.groupEnd();
  }
  initializeEvents() {
    console.group('events');
    console.log('Initialising events');
    console.groupEnd();
  }
}
let myClass = new MyClass(false);
複製程式碼

很多工作和程式碼在除錯資訊上可能並不是那麼有用。但是仍然是一個有意思的辦法,同時你可以看到它使你列印的上下文是多麼的清晰。

關於這個,還有最後一點需要說明,那就是 console.groupCollapsed。功能上和 console.group 一樣,但是分組塊一開始是摺疊的。它沒有得到很好的支援,但是如果你有一個無意義的龐大的分組並想預設隱藏它,可以試試這個。

結語

這裡真的沒有過多的總結。在你可能只想得到比 console.log(pet) 的資訊更多一點,並且不太需要偵錯程式的時候,上面這些工具都可能幫到你。

也許最有用的是 console.table,但是其他方法也都有其適用的場景。在我們想要除錯一些東西時,我熱衷於使用 console.assert,但那也只在某種特殊情況下。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄