不只 console.log#
Debug JavaScript 最直覺的方式是 console.log,但 Console API 還有非常多其他 method,每個都有適合的使用場景。多數時候 console.log 確實能解決問題,可是用對 method 可以省下不少時間,本章挑幾個原作者最常用的成員來介紹。
console.assert#
console.assert 的作用接近 console.log,最大的差別是:只有第一個參數是 falsy 時才會輸出。
JavaScript 的 falsy 值清單:
[false, null, undefined, 0, -0, 0n, NaN, ""];舉例:要檢查 user.name 是否為空,常見寫法是:
const user = {
name: "",
};
if (!user.name) {
console.log("哪邊出錯了 QQ", user);
}用 console.assert 可以省掉 if:
console.assert(user.name, "哪邊出錯了 QQ", user);語意上更清晰:「條件不符就拋警告」。
唯一要小心的是條件方向:第一個參數是 falsy 時才會作用,條件寫反了會完全沒反應,反而讓你以為一切正常。
console.count#
console.count(label) 印出某個 label 被執行了幾次,預設 label 是 default。
最簡單的用法:
function count(arg) {
console.count(arg);
}
count("foo");
count("bar");
count("bar");也能用來統計多種狀況的出現次數:
for (let i = 0; i < 5; i++) {
const int = Math.ceil(Math.random() * 100);
if (int < 20) console.count("太高了");
if (int > 20) console.count("太低了");
}每次呼叫對應 label 的計數會自動 +1,比自己維護一個 counter 變數方便很多。
console.countReset#
跟 console.count 是一對,把指定 label 的計數歸零。常見用法是計算「單次行為」會觸發幾次,例如 React 中按下按鈕後總共觸發了幾次 render。
同步的 setState 會被 React batch 在一起,但非同步狀態下每個 setState 都會觸發 render:
function App() {
const [count, setCount] = React.useState(0);
const [count2, setCount2] = React.useState(0);
const [asyncCount, setAsyncCount] = React.useState(0);
const [asyncCount2, setAsyncCount2] = React.useState(0);
const onClick = () => {
console.countReset("render");
setCount(count + 1);
setCount2(count2 + 1);
Promise.resolve().then(() => {
setAsyncCount(asyncCount + 1);
setAsyncCount2(asyncCount2 + 1);
});
};
console.count("render");
return (
<div onClick={onClick}>
<h1>Hello, please click me.</h1>
<h2>{count}</h2>
</div>
);
}按下按鈕後可以看到 ‘render’ 計數從 1 跳到 3,代表這次互動觸發了 3 次 render。
console.group#
為了在大量 log 中一眼看到自己的訊息,是不是寫過這種程式碼:
console.log("---------");
console.log(object);
console.log("---end---");console.group 是更乾淨的解法,可以給 log 加上可摺疊的標籤,也能多層巢狀:
console.group("Start debugging");
console.log("de-");
console.group("Nested");
console.warn("deeper message");
console.groupEnd();
console.log("bug");
console.groupEnd();每呼叫一次 console.group 就要對應一次 console.groupEnd。另外還有 console.groupCollapsed,差別只在預設是收合狀態,需要手動展開。
console.table#
如果只能在這些 Console API 裡推一個,原作者選
console.table。
要印出陣列裡的物件時,console.log 必須一層一層展開,非常不方便。試試以下資料:
const rows = [
{
name: "Frozen yoghurt",
calories: 159,
fat: 6,
carbs: 24,
protein: 4,
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9,
carbs: 37,
protein: 4.3,
},
{
name: "Eclair",
calories: 262,
fat: 16,
carbs: 24,
protein: 6,
},
];用 console.log(rows) 會印出收合的物件陣列;按住 Option / Alt 點開會把所有屬性(包括 __proto__)展開,畫面非常雜。
換成 console.table(rows),會用表格呈現所有物件,欄位寬度可以拖拉調整,還能用第二個參數限制顯示欄位:
console.table(rows, ["name", "fat"]);console.table 還解決另一個 console.log 的陷阱:物件被印出後,如果之後還會被修改,「展開」物件時看到的是當下值,而不是 log 當時的值。例子:
const animal = {
name: "mimi",
type: "cat",
other: {
emoji: "?",
sound: "meow",
},
};
console.log(animal);
animal.name = "ami";之後手動展開 log 出的物件,會看到 name: 'ami',跟印出當下不一致。console.log 的右上角其實有個小圖示,hover 上去會提示 Value below was evaluated just now.,明確說了「展開時的值」≠「當時的值」。
console.table 會在執行當下就把資料 snapshot 成表格,自然避開這個問題。
物件層級很深的時候,
JSON.stringify(obj, null, 2)也是不錯的選擇,直接把整個物件以縮排 JSON 字串呈現。
console.time#
要量某段程式碼或使用者行為的執行時間,常看到這種寫法:
const t0 = performance.now();
alert("Hello World!");
const t1 = performance.now();
alert("Another Hello World!");
console.log(`Spent: ${t1 - t0} ms`);
const t2 = performance.now();
console.log(`Spent: ${t2 - t0} ms`);繁瑣又容易寫錯。改用 console.time:
console.time("Spent");
alert("Hello World!");
console.timeLog("Spent");
alert("Another Hello World!");
console.timeEnd("Spent");用法:
console.time(label):啟動一個計時器console.timeLog(label):印出目前已經過了多久(可以呼叫無限多次)console.timeEnd(label):停止計時器並印出總時間
如果 timeLog 或 timeEnd 用了沒啟動過的 label,DevTools 會印 warning。
console.trace#
當問題涉及多個 function 或第三方套件、同一個 function 在多處被呼叫時,console.log 只能看到「印出當下的位置」,不夠用。console.trace 會直接印出調用堆疊(Call Stack)並展開:
function a() {
console.trace();
}
function b() {
a();
}
function c() {
b();
}
b();
c();實戰例子:使用某影片播放器套件時,影片偶爾會莫名暫停。要追是哪段程式碼呼叫了 pause,可以這樣:
video.addEventListener("pause", console.trace);馬上就能看出是套件內哪個 function 觸發暫停,比把 console.log 撒到處都是有效率很多。
銜接#
console.log 大法之外,這些 Console API 是日常 debug 的好搭檔。下一章進入 Console Utilities API:那些不能用在 production 程式碼、但在 Console panel 內超強的 utility function($_、$0、$x、$$、debug、monitor 等)。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10240826