JavaScript 課程 – 錯誤與除錯(Error Handling & Debugging)
單元:瀏覽器開發者工具
簡介
在前端開發的過程中,程式錯誤是不可避免的。即使是最有經驗的開發者,也會因為瀏覽器差異、非同步流程或是 DOM 操作失誤而產生錯誤。若錯誤只能靠 console.log() 逐行印出來偵測,除錯的效率會非常低下,且容易忽略隱藏在複雜呼叫堆疊中的根本原因。
現代瀏覽器(如 Chrome、Firefox、Edge)內建的 開發者工具(DevTools),提供了即時檢視、斷點除錯、效能分析與網路請求監控等功能,讓我們能在瀏覽器內部直接觀測程式執行狀態,快速定位問題。掌握這套工具,不僅能縮短除錯時間,還能提升程式碼品質與維護性,是每位 JavaScript 開發者的必備技能。
核心概念
1. Console 面板與基本輸出
Console 是最常被使用的除錯入口。除了 console.log(),還有 console.warn()、console.error()、console.table() 等多種方法,能協助我們分層呈現訊息。
// 基本訊息
console.log('程式開始執行');
// 警告訊息(會以黃色標示)
console.warn('此變數即將被棄用');
// 錯誤訊息(會以紅色標示,並顯示堆疊)
console.error('發生未捕捉的例外', new Error('範例錯誤'));
// 以表格方式顯示陣列或物件集合
const users = [
{ id: 1, name: 'Alice', age: 28 },
{ id: 2, name: 'Bob', age: 34 }
];
console.table(users);
小技巧:在 Chrome DevTools 中,
console.log會自動保留物件的「參照」,即使物件稍後被修改,點擊已列出的物件仍會顯示最新值。若想凍結當下快照,可使用console.log(JSON.parse(JSON.stringify(obj)))。
2. 斷點(Breakpoints)與逐行執行
在 Sources(或 Debugger)面板,我們可以直接在 JavaScript 檔案的任意行點擊左側的行號,設定 斷點。當程式執行到斷點時會暫停,並允許我們檢視變數、呼叫堆疊與執行環境。
2.1 常見斷點類型
| 斷點類型 | 說明 |
|---|---|
| 行斷點 | 最基本的斷點,直接在程式碼行上設定。 |
| 條件斷點 | 只有當條件成立時才會觸發。右鍵點擊行號 → “Add conditional breakpoint”。 |
| 例外斷點 | 在 Sources → Pause on exceptions 開啟,可選擇 “Pause on caught exceptions”。 |
| DOM 斷點 | 監聽特定 DOM 元素的屬性變更、移除或子節點變化。右鍵元素 → “Break on → attribute modifications”。 |
function fetchData(id) {
// 設定條件斷點:只在 id 為負數時暫停
if (id < 0) throw new Error('ID 必須是正整數');
return fetch(`https://api.example.com/item/${id}`)
.then(res => res.json());
}
// 呼叫範例
fetchData(-5); // 會在條件斷點觸發
2.2 逐行執行與即時編輯
當程式在斷點暫停後,工具列提供 Step over (F10)、Step into (F11)、Step out (Shift+F11) 等操作,讓我們逐行觀察程式流。
此外,在 Scope 區塊中可以直接編輯變數值,測試不同情境而不必重新整理頁面。
3. 觀察 Call Stack(呼叫堆疊)
當錯誤發生或程式暫停時,Call Stack 會列出目前執行的函式路徑。點擊任意堆疊項目,即可跳轉到對應的程式碼位置,快速追溯錯誤來源。
function a() { b(); }
function b() { c(); }
function c() { throw new Error('意外錯誤'); }
a(); // 執行後會在 c() 拋出例外,Call Stack 會顯示 a → b → c
4. Network 面板:監控 HTTP 請求
大多數前端功能依賴 Ajax、Fetch 或 WebSocket,Network 面板可以即時看到每筆請求的 URL、方法、狀態碼、請求與回應標頭、載荷與時長。
- 篩選:只顯示 XHR、Fetch 或 WS 請求。
- 預覽:點擊請求後可查看 JSON、HTML 或二進位的回應內容。
- 重放:右鍵請求 → “Copy → Copy as cURL”,方便在終端機重測。
// 範例:使用 fetch 取得資料,同時在 Network 面板觀察
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(r => r.json())
.then(data => console.log(data))
.catch(err => console.error(err));
5. Performance(效能)面板:找出卡頓與重繪
Performance 面板能錄製一段時間內的瀏覽器活動,將 JavaScript 執行、樣式計算、佈局(layout)與繪製(paint)分開顯示。
- 長任務(Long Task):超過 50 ms 的任務會以紅色標示,提示可能造成 UI 卡頓。
- FPS:畫面更新率低於 60 fps 時,會在右上角顯示警示。
透過 Timeline,我們可以定位哪段程式碼導致主執行緒阻塞,進而優化(例如使用 requestIdleCallback、Web Worker 或拆分大型迴圈)。
程式碼範例
以下提供 4 個實務中常見的除錯情境,示範如何結合 DevTools 完成排錯。
範例 1:條件斷點解決資料遺失問題
function renderList(items) {
// 假設 items 來源於 API,偶爾會出現 undefined
items.forEach(item => {
// 設定條件斷點:只在 item 為 undefined 時暫停
if (item === undefined) debugger; // <-- 直接在程式碼內加入 debugger
const li = document.createElement('li');
li.textContent = item.name;
document.querySelector('#list').appendChild(li);
});
}
// 測試呼叫
fetch('https://api.example.com/list')
.then(r => r.json())
.then(data => renderList(data));
說明:在 debugger; 行加入條件斷點,當 item 為 undefined 時程式會自動暫停,開發者可即時檢查 API 回傳值或前置資料處理流程。
範例 2:例外斷點捕捉非同步錯誤
async function loadUser(id) {
const res = await fetch(`/user/${id}`);
// 假設伺服器在 404 時仍回傳 200,導致後續程式碼拋出錯誤
const data = await res.json(); // 若回傳非 JSON,會拋出 SyntaxError
return data;
}
// 開啟「Pause on exceptions」後執行
loadUser(9999).catch(err => console.error('載入失敗', err));
說明:啟用 Pause on uncaught exceptions,當 res.json() 解析失敗時,DevTools 會立即在該行暫停,讓開發者檢視 res 的實際內容與 HTTP 狀態碼。
範例 3:Network 面板偵測 CORS 問題
fetch('https://othersite.com/api/data')
.then(r => r.json())
.then(d => console.log(d))
.catch(e => console.error('CORS 錯誤', e));
操作步驟:
- 開啟 Network → 勾選 Disable cache。
- 重新載入頁面,觀察該請求的 Status 是否為
200,或是CORS錯誤(Access-Control-Allow-Origin缺失)。 - 若顯示
CORS policy: No 'Access-Control-Allow-Origin' header,則可在 Headers 中確認伺服器回傳的 CORS 設定。
範例 4:Performance 面板找出長任務
// 假設此函式在 UI 互動時被呼叫,會造成卡頓
function heavyComputation() {
const start = Date.now();
while (Date.now() - start < 150) {
// 模擬 150ms 的同步阻塞
// 內部沒有任何 I/O,會佔用主執行緒
}
console.log('計算完成');
}
// 按鈕點擊觸發
document.querySelector('#run').addEventListener('click', heavyComputation);
除錯流程:
- 開啟 Performance → 按下 Record,點擊按鈕觸發。
- 停止錄製後,在 Timeline 中看到 Long Task(紅色)標示,點擊可定位到
heavyComputation。 - 改寫為非阻塞方式(
setTimeout、requestIdleCallback)或 Web Worker,即可消除卡頓。
常見陷阱與最佳實踐
| 陷阱 | 為何會發生 | 解決方式 |
|---|---|---|
| 忘記關閉「Pause on exceptions」 | 除錯完畢後未關閉,導致正常執行時仍被中斷。 | 完成除錯後,點擊右上角的紅點或使用快捷鍵 Ctrl+Shift+M(Chrome)關閉。 |
過度依賴 console.log |
大量輸出會拖慢執行,且訊息容易被覆蓋。 | 使用斷點與 Watch 功能,僅在需要時才使用 console.debug(可在 DevTools 中過濾)。 |
| 忽視非同步堆疊 | Promise 拋出的錯誤只顯示在微任務隊列,堆疊不完整。 | 在 Chrome 110+ 以上,開啟 Async stack traces(Settings → Preferences → Debugger)。 |
在 Production 直接保留 debugger; |
使用者會看到斷點,甚至導致程式卡住。 | 建議在建置流程(Webpack、Rollup)使用 babel-plugin-transform-remove-debugger 移除。 |
| 未設定網路請求的超時 | 請求卡住會讓除錯變得困難。 | 使用 AbortController 或第三方庫(axios)設定 timeout,並在 Network 面板觀察 canceled 狀態。 |
最佳實踐
- 先定位,再修正:先利用斷點與 Call Stack 找出問題根源,避免直接在多處修改程式碼。
- 保持 Console 整潔:在開發階段使用
console.group()、console.groupCollapsed()把相關訊息歸類,最終上線前移除或關閉。 - 善用條件斷點:避免在大量迴圈中每一次都暫停,只在特定條件成立時才觸發。
- 結合 Performance 面板:即使功能正確,也要檢查是否有長任務或過度重繪,提升使用者體驗。
- 版本控制除錯設定:將 DevTools 設定(如「Pause on exceptions」)寫入
.vscode/launch.json或 Chrome 的「Workspace」設定,讓團隊成員共享相同除錯環境。
實際應用場景
1. SPA(Single Page Application)路由錯誤
在 React、Vue 或 Angular 等框架中,路由切換失敗常因 懶載入(lazy load) 的模組未正確返回 Promise。利用 Network 觀察對應的 JS Chunk 是否成功下載,配合 Sources → Pause on exceptions 找出 import() 拋出的錯誤,即可快速定位是 CDN 設定問題還是模組路徑錯誤。
2. 表單驗證與即時回饋
當使用者輸入表單時,常會在 input 事件裡做即時驗證。若驗證函式拋出例外,整個 UI 可能會凍結。使用 Event Listener Breakpoints(右鍵 → Break on → Event Listener)在 input 事件上設定斷點,可即時觀察變數狀態與錯誤訊息,確保驗證邏輯不會破壞主執行緒。
3. 第三方腳本衝突
引入廣告、分析或社群外掛時,常會出現全域變數覆寫或事件冒泡問題。開啟 Sources → Call Stack → Async,觀察外部腳本的執行順序,結合 Console 的 monitorEvents(document, 'click') 監控點擊事件的傳遞路徑,快速找出衝突根源。
4. WebSocket 實時資料流
在即時聊天或遊戲應用中,WebSocket 訊息若未正確解析會導致 UI 異常。使用 Network → WS 分頁查看每筆訊息的原始內容,並在 Sources 中對 socket.onmessage 加上 Conditional breakpoint(如 event.data.includes('error')),即可即時捕捉錯誤訊息。
總結
瀏覽器開發者工具是 JavaScript 開發者最強大的「除錯武器」:
- Console 提供多層次的訊息輸出與即時檢視。
- Sources 的斷點、條件斷點與 Call Stack,讓我們能在程式執行的任何時刻「凍結」畫面,觀測變數與流程。
- Network 讓 HTTP、WebSocket 請求的細節一目了然,快速定位 CORS、超時與回傳格式問題。
- Performance 能揭露長任務與重繪瓶頸,協助我們寫出更流暢的使用者體驗。
掌握這些工具的使用方式,並遵循 條件斷點、非同步堆疊追蹤、效能監測 等最佳實踐,能大幅提升除錯效率,減少因錯誤而產生的開發與維護成本。未來在面對更大型的前端專案或複雜的即時應用時,這套除錯流程將成為你穩定交付高品質程式碼的基礎。
祝你在 JavaScript 的除錯旅程中,不再被錯誤絆倒,而是以快速、精準的方式將它們捕捉、解決! 🚀