本文 AI 產出,尚未審核

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; 行加入條件斷點,當 itemundefined 時程式會自動暫停,開發者可即時檢查 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));

操作步驟

  1. 開啟 Network → 勾選 Disable cache
  2. 重新載入頁面,觀察該請求的 Status 是否為 200,或是 CORS 錯誤(Access-Control-Allow-Origin 缺失)。
  3. 若顯示 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);

除錯流程

  1. 開啟 Performance → 按下 Record,點擊按鈕觸發。
  2. 停止錄製後,在 Timeline 中看到 Long Task(紅色)標示,點擊可定位到 heavyComputation
  3. 改寫為非阻塞方式(setTimeoutrequestIdleCallback)或 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 狀態。

最佳實踐

  1. 先定位,再修正:先利用斷點與 Call Stack 找出問題根源,避免直接在多處修改程式碼。
  2. 保持 Console 整潔:在開發階段使用 console.group()console.groupCollapsed() 把相關訊息歸類,最終上線前移除或關閉。
  3. 善用條件斷點:避免在大量迴圈中每一次都暫停,只在特定條件成立時才觸發。
  4. 結合 Performance 面板:即使功能正確,也要檢查是否有長任務或過度重繪,提升使用者體驗。
  5. 版本控制除錯設定:將 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,觀察外部腳本的執行順序,結合 ConsolemonitorEvents(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 的除錯旅程中,不再被錯誤絆倒,而是以快速、精準的方式將它們捕捉、解決! 🚀