首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau
百百 BLOG
首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau

Kurau Blog

「隨心而寫,真真假假,都是我」

一個記錄生活、輸出興趣的個人空間。
遊戲、影視、閱讀、學習……每一段體驗都值得留下文字。

頁面導覽

  • 學習紀錄
  • 遊戲心得
  • 影視Life
  • 書單
  • 委託作品與二創
  • Kurau
  • 合作邀請

找到我

歡迎來 Discord 找我聊天!

“曾經發生的事不可能忘記,只是暫時想不起來而已。”-《神隱少女》

© 2026 Kurau All rights reserved

面試考題

throw Error用法

By Kurau·Updated 2026-06-02·8 分鐘閱讀

  • throw Error 是用來丟擲錯誤,便於程式處理錯誤,如果你程式裡沒有 try-catch 或者是 Promise reject,那最終這個錯誤會向上傳遞,直到被 try-catch 或者是 Promise reject 捕捉到,如果最後還沒有被捕捉到,那最終錯誤會被輸出到控制檯。
  • 可以 throw 別的東西,不僅限於 Error,可以是任何資料型別,例如字串、數字、物件等。

舉例:

javascriptCopy code
try {
  throw new Error("something went wrong");
} catch (e) {
  console.error(e);
}
JavaScript
javascriptCopy code
try {
  throw "something went wrong";
} catch (e) {
  console.error(e);
}
JavaScript

下面有更好的throw Error例子

try-catch 範例

function divide(a, b) {
  if (b === 0) {
    throw new Error("Cannot divide by zero.");
  }
  return a / b;
}

try {
  const result = divide(5, 0);
  console.log(result);
} catch (error) {
  console.error("Error:", error.message);
}

// 輸出:
// Error: Cannot divide by zero.

// 如果没有使用 try-catch 包起来,那错误将会向上传递,直到被全局的 try-catch 捕获到,或者在未被捕获到的情况下,将被显示在控制台上。
// 因此,如果没有 try-catch 或全局的 try-catch,在代码执行完后,你将会看到一条错误信息,表明程序出现了错误。
JavaScript

Promise reject 範例

const promise = new Promise((resolve, reject) => {
  const divide = (a, b) => {
    if (b === 0) {
      reject(new Error("Cannot divide by zero."));
    }
    resolve(a / b);
  };

  divide(5, 0);
});

promise.then((result) => {
  console.log(result);
}).catch((error) => {
  console.error(error.message);
});
JavaScript

為什麼建議丟 Error 而不是亂丟值

  • throw 後面可以接「任何值」,但強烈建議丟 Error(或其子類別)的實例。
  • 理由:
    • Error 物件會自動帶 stack trace(堆疊追蹤),debug 時能看到錯誤從哪一行冒出來;丟字串就沒有。
    • 很多工具 / library / log 系統預期接到的是 Error,會去讀 .message、.stack,丟字串會讓它們拿到 undefined。
    • catch 端可以用 instanceof 判斷錯誤型別,丟字串就只能用 typeof 硬猜。
// ❌ 不建議:沒有 stack,難以追蹤
throw "資料庫連線失敗";

// ✅ 建議:有 message + stack + name
throw new Error("資料庫連線失敗");
js
常見陷阱
因為什麼都能 throw,catch (e) 接到的 e 型別不保證是 Error。直接讀 e.message 在「別人丟了字串」的情況下會拿到 undefined。寫防禦性程式碼時要先判斷:
catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  console.error(msg);
}
js

Error 物件的結構

Error 是 JS 內建的建構式,產生的物件主要有三個屬性:

屬性說明
message你傳進建構式的字串,描述錯誤內容
name錯誤的型別名稱,預設 "Error",子類別會是 "TypeError" 等
stack堆疊追蹤字串(非標準但所有主流引擎都有),含檔名 / 行號
const err = new Error("出事了");
console.log(err.message); // "出事了"
console.log(err.name);    // "Error"
console.log(err.stack);   // "Error: 出事了\n    at ... (file.js:1:13)"
console.log(err.toString()); // "Error: 出事了"  → name + ": " + message
js

ES2022 之後還支援 cause,用來包裝底層錯誤(保留原始錯誤鏈):

try {
  await fetchUser();
} catch (e) {
  throw new Error("載入使用者失敗", { cause: e });
}
js

內建的 Error 子類別

JS 內建幾種 Error 子類別,引擎在特定情況會自動丟出來,面試常考它們的觸發時機:

子類別什麼時候會被丟出
TypeError值的型別不對,例如呼叫 null.foo()、把非函式當函式呼叫
RangeError數值超出合法範圍,例如 new Array(-1)、遞迴爆 stack
ReferenceError存取一個沒宣告的變數
SyntaxError語法錯誤,例如 JSON.parse("{bad}")
URIErrordecodeURIComponent("%") 這種 URI 處理函式參數錯誤
EvalError與 eval() 相關(現代幾乎不會遇到,保留為歷史相容)
null.foo();              // TypeError: Cannot read properties of null
notDefined;              // ReferenceError: notDefined is not defined
new Array(-1);           // RangeError: Invalid array length
JSON.parse("{bad}");     // SyntaxError: Unexpected token b in JSON
js

這些子類別都繼承自 Error,所以 e instanceof Error 對它們都成立:

try {
  null.foo();
} catch (e) {
  console.log(e instanceof TypeError); // true
  console.log(e instanceof Error);     // true
}
js

自訂 Error class

實務上會針對不同錯誤情境定義 自訂 Error class,讓 catch 端能用 instanceof 精準分流,而不是比對 message 字串(脆弱、易因改字而壞掉)。

class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = "ValidationError"; // 一定要設,否則 name 會是 "Error"
    this.field = field;            // 可以加自訂屬性
  }
}

function createUser(data) {
  if (!data.email) {
    throw new ValidationError("email 為必填", "email");
  }
}

try {
  createUser({});
} catch (e) {
  if (e instanceof ValidationError) {
    console.error(`欄位 ${e.field} 驗證失敗:${e.message}`);
  } else {
    throw e; // 不是我認識的錯誤,繼續往上丟(不要吞掉)
  }
}
js
重點
  • super(message) 一定要呼叫,才能正確設定 message 與 stack。
  • 記得手動設 this.name,預設會是 "Error",log 出來會看不出是哪種錯。
  • TypeScript 中繼承 Error 時,若 target 設為 ES5,instanceof 可能失效,需要在 constructor 加 Object.setPrototypeOf(this, ValidationError.prototype)。

TypeScript 版本(順便標型別):

class ValidationError extends Error {
  constructor(
    message: string,
    public readonly field: string,
  ) {
    super(message);
    this.name = "ValidationError";
  }
}
ts

try / catch / finally

完整的錯誤處理結構有三塊,catch 與 finally 至少要有一個:

try {
  // 可能丟錯的程式碼
  doSomething();
} catch (error) {
  // 只有 try 區塊丟錯時才會進來
  handle(error);
} finally {
  // 不管有沒有錯、有沒有 return,都一定會執行
  cleanup();
}
js
  • try:放可能出錯的程式碼。
  • catch (error):捕捉錯誤。ES2019 起參數可省略(catch {}),用在你不在乎錯誤內容時。
  • finally:無論成功、失敗、甚至 try/catch 裡有 return,都會執行,常用來釋放資源(關連線、清 timer)。
finally 的陷阱
如果 finally 裡有 return,它會覆蓋掉 try / catch 裡的 return,甚至吞掉正在往上丟的錯誤:
function f() {
  try {
    return 1;
  } finally {
    return 2; // 最終回傳 2,try 的 1 被蓋掉!
  }
}
f(); // 2
js

所以 finally 裡盡量只做清理,不要寫 return / throw。

async / Promise 的錯誤處理

非同步是面試重災區,重點在「錯誤怎麼傳遞、怎麼接」。

Promise:用 .catch 或 reject

fetchData()
  .then((data) => process(data))
  .catch((err) => console.error(err)); // 鏈上任一環 reject 或 throw 都會掉到這
js

.then 的 callback 裡 throw 等同於回傳一個 rejected Promise,會被後面的 .catch 接住。

async/await:用 try-catch

async function load() {
  try {
    const res = await fetch("/api/user");
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}`); // fetch 本身不會因 4xx/5xx 而 reject,要自己丟
    }
    return await res.json();
  } catch (err) {
    console.error("載入失敗:", err);
    throw err; // 視情況決定要吞還是往上丟
  }
}
js
async 常見陷阱
  1. 忘記 await:沒 await 的 async function 回傳的是 Promise,裡面 throw 的錯不會被外層 try-catch 接到,會變成 unhandled rejection。
    try {
      load(); // ❌ 少了 await,這裡的 try-catch 接不到 load 內部的錯
    } catch (e) { /* 永遠不會進來 */ }
    
    js
  2. fetch 不會因 HTTP 錯誤碼 reject:只有網路層失敗(斷線、CORS)才 reject,4xx/5xx 要自己檢查 res.ok 再 throw。
  3. forEach 裡的 async:array.forEach(async () => { await ... }) 不會等待,錯誤也接不到,要改用 for...of + await 或 Promise.all。

多個 Promise 的錯誤

方法行為
Promise.all任一個 reject 就整體 reject(fail-fast)
Promise.allSettled全部跑完,回傳每個的成功 / 失敗結果,不會 reject
Promise.race第一個 settle(成功或失敗)的結果決定整體
Promise.any第一個成功的決定整體;全部失敗才 reject(AggregateError)

最佳實務

  • 不要吞錯(swallow error):空的 catch {} 或只 catch (e) {} 什麼都不做,會讓 bug 無聲消失,是最該避免的反模式。
    // ❌ 最糟:錯誤被吞,之後完全查不到原因
    try { risky(); } catch (e) {}
    
    // ✅ 至少 log,並決定要不要往上丟
    try {
      risky();
    } catch (e) {
      logger.error(e);
      throw e; // 不確定能處理就往上丟,別自作主張
    }
    
    js
  • 只 catch 你能處理的錯:認不得的錯誤就 throw e 繼續往上拋,交給有能力處理的層級。
  • 用 instanceof 分流,別比對 message 字串:message 是給人看的,隨時會改;型別才是穩定的判斷依據。
  • 丟有意義的 Error:message 寫清楚情境,必要時用自訂 class + cause 保留錯誤鏈。
  • 資源清理放 finally:確保連線 / 檔案 / timer 一定被釋放。
  • 非同步一律 await + try-catch,或 Promise 一定接 .catch,避免 unhandled rejection。

目錄

    ◆ 相關文章

    • 會try{}catch{}嗎

      2026-05-09
    • Async function-Await 函式

      2026-06-02
    • TypeScript 特性 - Interface

      2026-06-02
    • TypeScript 資料型別 - 元組(Tuple) & 列舉(Enum)

      2026-06-02
    ← 上一篇Async function-Await 函式下一篇 →TypeScript 特性 - Interface

    ◆ 關於作者

    Kurau

    個人寫作 / 創作的 SoT,記錄遊戲、影視、學習與生活。

    更多 Kurau 的文章