try...catch 語法

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.

try...catch 語法標記出一整塊需要測試的語句,並指定一個以上的回應方法,萬一有例外拋出時,try...catch 語句就會捕捉。

try...catch 語法由 try 區塊所組成,其中內含一個以上的語句,和零個以上的 catch 區塊,其中內含語句用來指明當例外在 try 區塊裡拋出時要做些什麼。也就是當你希望 try 區塊成功,但如果他不成功時,你會想要把控制權移交給 catch 區塊。如果任何在 try 區塊內部裡的語句(或者在 try 區塊內部呼叫的函數裡)拋出例外,控制權將立即轉移給 catch 區塊。如果沒有例外從 try 區塊裡拋出,就會跳過 catch 區塊。finally 區塊會在 trycatch 區塊執行之後才執行,但會在 try...catch 語法後面的語句之前執行。

以下範例使用 try...catch 語法。本範例呼叫函數,這個函數是用來在陣列裡根據傳給函數的值來查詢月份的名稱。如果傳入的值不符合月份的數字 (1-12),就會拋出值為 InvalidMonthNo 的例外,而且在 catch 區塊裡的語句會把 monthName 變數設定為 unknown

js
function getMonthName(mo) {
  mo = mo - 1; // 針對陣列索引調整月份的數字 (1=Jan, 12=Dec)
  var months = new Array(
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
  );
  if (months[mo] != null) {
    return months[mo];
  } else {
    throw "InvalidMonthNo";
  }
}

try {
  // 需要測試的語句
  monthName = getMonthName(myMonth); // 可拋出例外的函數
} catch (e) {
  monthName = "unknown";
  logMyErrors(e); // 把例外物件傳給錯誤處理器
}

catch 區塊

你可以使用單一的 catch 區塊來處理所有在 try 區塊裡可能會產生的例外,或者你也可以使用分離的 catch 區塊,每一個皆各自處理特定的例外類型。

單一 catch 區塊 使用單一 try...catch 語法的 catch 區塊針對在 try 區塊裡拋出的所有例外來執行錯誤處理的代碼。

單一的 catch 區塊語句如下︰

js
catch (catchID) {
  statements
}

catch 區塊所指定的識別子(前面語句裡的 catchID)持有由 throw 語法所指定的值。你可以使用這個識別子來取得有關於被拋出的例外的資訊。當進入 catch 區塊時,JavaScript 就會建立這個識別子。識別子只能在 catch 區塊的期間內持續作用。catch 區塊執行結束以後,識別子就不再能使用。

舉例來說,以下代碼會拋出例外。當例外出現的時候,控制權就轉移給 catch 區塊。

js
try {
  throw "myException"; // 產生例外
} catch (e) {
  // 用來處理任何例外的語句
  logMyErrors(e); // 把例外物件傳給錯誤處理器
}

多重 catch 區塊 單一的 try 語句可以對應複數個有前提條件的 catch 區塊,每一個皆可處理特定的例外的類型。於是,當指定的的例外被拋出時,就只會進入適當條件的 catch 區塊。你也可以針對所有未指定的例外,使用選用性的對應所有例外的 catch 區塊來作為語法裡最後一個的 catch 區塊。

舉例來說,以下函數呼叫三個其他的函數(已在別處定義了)來檢驗自己的參數。如果檢驗函數判斷出他所要檢驗的元素是無效的話,他就返回 0,導致呼叫者拋出對應的例外。

js
function getCustInfo(name, id, email) {
  var n, i, e;

  if (!validate_name(name)) throw "InvalidNameException";
  else n = name;
  if (!validate_id(id)) throw "InvalidIdException";
  else i = id;
  if (!validate_email(email)) throw "InvalidEmailException";
  else e = email;
  cust = n + " " + i + " " + e;
  return cust;
}

有各種條件的 catch 區塊會把控制權安排給適當的例外處理器。

js
try {
// 可以拋出三個例外的函數
   getCustInfo("Lee", 1234, "lee@netscape.com")
}

catch (e if e == "InvalidNameException") {
// 針對無效的名稱呼叫處理器
   bad_name_handler(e)
}

catch (e if e == "InvalidIdException") {
// 針對無效的 ID 呼叫處理器
   bad_id_handler(e)
}

catch (e if e == "InvalidEmailException") {
// 針對無效的電子郵件位址呼叫處理器
   bad_email_handler(e)
}

catch (e){
// 不知道該做什麼,就記在日誌裡
   logError(e)
}

finally 區塊

finally 區塊內含的語句,會在 try 和 catch 區塊執行以後、並在 try...catch 語法後面的語句之前來執行。無論有沒有被拋出的例外,finally 區塊都會執行。如果有被拋出的例外,即使沒有 catch 區塊來處理這些例外,還是會執行 finally 區塊裡的語句。

當出現例外時,你可以使用 finally 區塊來使你的 Script 優美的停止。舉例來說,你可能需要釋放 Script 所佔用的資源。以下範例開啟了檔案,並執行使用這個檔案(伺服端的 JavaScript 可讓你存取檔案)的語句。如果在開啟檔案時有例外被拋出,finally 區塊會在 Script 停止之前把檔案關閉。

js
openMyFile();
try {
  writeMyFile(theData); // 這裡有可能拋出錯誤
} catch (e) {
  handleError(e); // 如果我們得到錯誤,就處理他
} finally {
  closeMyFile(); // 永遠會關閉這項資源
}

try...catch 語法的嵌套

你可以嵌套一個以上的 try...catch 語法。如果有一個內部的 try...catch 語法沒有 catch 區塊,圍住這些 try...catch 語法的 catch 區塊就會被用來比對。

Error 物件的用處

根據錯誤的類型,你有可能使用 「name」 和 「message」 屬性來取得更多明確的訊息。「name」 提供錯誤的一般類別(例如,「DOMException」 或 「Error」),「message」 通常提供更為簡練的訊息,如此就能把錯誤物件轉換為字串來取得訊息。

如果你要拋出你自己的例外,以從這些屬性取得好處(例如,如果你的 catch 區塊不區分你自己的例外和系統的例外的話),你可以使用錯誤建構子。例如︰

js
function doSomethingErrorProne () {
   if (ourCodeMakesAMistake()) {
      throw (new Error('The message'));
   }
   else {
      doSomethingToGetAJavascriptError();
   }
}
....
try {
   doSomethingErrorProne();
}
catch (e) {
   alert(e.name);// 警報 'Error'
   alert(e.message); // 警報 'The message' 或 JavaScript 錯誤訊息
}