Please note, this is a STATIC archive of website developer.mozilla.org from November 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

控制流與例外處理

正在翻譯中。

JavaScript擁有許多陳述句,特別是控制流的陳述,你可以用這些陳述來增加你程式的互動性。這個章節將會概要介紹陳述。

JavaScript參考 中有比本章更多關於陳述句的細節. 在Javascript程式碼中,分號(;)被用來隔開陳述句.

任何JavaScript表達式也是一個陳述。 有關表達式的完整信息,請參閱表達式與運算元.

程式區塊陳述

最基本的陳述是"用於將陳述分塊"的"區塊陳述".程式區塊被以一對大括號隔離開來:

{
  陳述1;
  陳述2;
  .
  .
  .
  陳述n;
}

範例

區塊陳述經常被用於控制流陳述(例如:if, for, while).

while (x < 10) {
  x++;
}

在這裏, { x++; }是區塊陳述.

重要: JavaScript 在 ECMAScript2015 以前的版本並沒有區塊範圍的概念. 在區塊中的變數有效範圍是包含該區塊的函式或者是執行檔,並且在區塊外也可使用它們.換句話說,區塊並不定義了範圍.Javascript中的"獨立"區塊將會產生與C和Java中不同的效果 .舉例來說:

var x = 1;
{
  var x = 2;
}
console.log(x); // 輸出 2

這裏輸出了2是因為區塊中變數"var x"陳述擁有與區塊外的"var x"相同的有效範圍.在 C o或 Java, 相同的程式碼將會輸出1 .

從 ECMAScript2015 版本起, 使用let定義的變數範圍將被限制於區塊內.

條件陳述句

條件陳述句是一些在指定條件爲真下將被執行的指令. JavaScript支援兩種條件陳述: if...else 和 switch.

if...else 陳述

if陳述將在"邏輯判斷爲真"下執行接下來的一個陳述.選擇性的else陳述將會在"邏輯判斷爲否"時被執行. if陳述的用法看起來如下:

if (指定條件) {
  陳述句1;
} else {
  陳述句2;
}

指定條件可以是任何會回傳true或false的表達式。參見 布爾值(Boolean) 來進一步瞭解哪些表達式會回傳 true 及 false。假如指定條件爲 true,陳述句 1 會被執行;否則,陳述句2 會被執行。陳述句1 及陳述句 2 可以是任何陳述,包含巢狀 if 陳述。

你也可以藉由 else if 來使用複合的陳述句來測試多種不同的條件,如下:

if (指定條件1) {
  陳述句1;
} else if (指定條件2) {
  陳述句2;
} else if (指定條件n) {
  陳述句n;
} else {
  最後陳述句;
} 

在多個條件中,只有第一個條件爲true的陳述會被執行.若要執行多個陳述,可以將陳述包在同一個程式區塊中({ ... }) . 通常來說,使用區塊陳述是很好的習慣,尤其在使用巢狀if的時候:

if (指定條件) {
  陳述句_1_執行_當_指定條件_爲_真;
  陳述句_2_執行_當_指定條件_爲_真;
} else {
  陳述句_3_執行_當_指定條件_爲_否;
  陳述句_4_執行_當_指定條件_爲_否;
}
建議不要在以賦值作爲條件表達式,因為"賦值"常常被和"等於"搞混. 例如, 不要寫出如下面的程式碼:
if (x = y) {
  /* 描述句 */
}

如果你真的需要以賦值作爲條件表達式, 一種常見的方式是額外在賦值式外面加上一組括號. 例如:

if ((x = y)) {
  /* 描述句 */
}

爲"否"的值

下列的值會被看作false

  • false
  • undefined
  • null
  • 0
  • NaN
  • 空字串 ("")

其他所有的值,包含所有物件,當被作為條件陳述句都會被視為True.

不要把"布林真假值"和"布林物件的真假值"弄混了. 例如:

var b = new Boolean(false);
if (b) // 這會是True
if (b == true) // 這會是false

範例

I在下面的範例中,函式 checkData 回傳 true 當 Text object 的長度爲三; 否則, 顯示出 alert 並回傳 false.

function checkData() {
  if (document.form1.threeChar.value.length == 3) {
    return true;
  } else {
    alert("請輸入恰好三個字元. " +
    document.form1.threeChar.value + " is not valid.");
    return false;
  }
}

switch 陳述

switch 陳述允許程式依表達式的不同值來選擇不同標籤. 假如表達式和標籤匹配,程式會執行標籤對應的陳述.範例如下:

switch (表達式) {
  case 標籤1:
    陳述句1
    [break;]
  case 標籤2:
    陳述句2
    [break;]
    ...
  default:
    陳述句
    [break;]
}

程序首先尋找一個標籤與expression的值匹配的case子句,然後將控制權轉移給該子句,執行與其相關的陳述句。 如果沒有找到匹配的標籤,程序將查找default子句(選擇性),如果找到,則將控制權轉移到該子句,執行關聯的陳述句。 如果沒有找到default子句,程序繼續在switch結束後的語句執行。 按照慣例,默認子句是最後一個子句,但並不硬性規定。

與每個case子句相關聯的break陳述句(選擇性)確保程序在發現匹配的陳述句之後退出switch,並在switch後的陳述句中繼續執行。 如果省略break,程序將繼續在switch陳述中的下一個陳述句執行。

範例

在下面範例中,如果變數fruittype為“Bananas”,程序將與“Bananas”匹配並執行相關語句。 當遇到break時,程序離開switch並執行switch後的陳述句。 如果省略break,也將執行case “Cherries”的陳述句。

switch (fruittype) {
  case "Oranges":
    console.log("Oranges are $0.59 a pound.");
    break;
  case "Apples":
    console.log("Apples are $0.32 a pound.");
    break;
  case "Bananas":
    console.log("Bananas are $0.48 a pound.");
    break;
  case "Cherries":
    console.log("Cherries are $3.00 a pound.");
    break;
  case "Mangoes":
    console.log("Mangoes are $0.56 a pound.");
    break;
  case "Papayas":
    console.log("Mangoes and papayas are $2.79 a pound.");
    break;
  default:
   console.log("Sorry, we are out of " + fruittype + ".");
}
console.log("Is there anything else you'd like?");

例外處理陳述

你可以用以 throw 陳述丟出例外,並以 try...catch 陳述處理之.

例外的形態

任何物件(object)都可以在JavaScript中被拋出。 然而,並非所有拋出的物件都相同。 雖然將數字或字串作為錯誤物件使用是相當常見的,但使用為此目的專門創造的一種例外物件類型通常更有效:

throw 陳述

使用throw語句拋出例外。當拋出例外時,你要指定包含在要拋出物件中的值:

throw expression;

您可以拋出任何表達式,而不僅僅是特定類型的表達式。 以下的程式碼會拋出一些不同類型的例外:

throw "Error2";   // 字串形態
throw 42;         // 數字形態
throw true;       // True/False
throw {toString: function() { return "我是物件!"; } };
筆記: 您可以在拋出例外時指定物件。 然後,可以在catch塊中引用對象的屬性。以下的範例創建一個類型為UserException的物件myUserException,並在throw語句中使用它。
// 創建類型爲 UserException 的物件
function UserException(message) {
  this.message = message;
  this.name = "UserException";
}

// 讓例外轉換成整齊的字串當它被當作字串使用時
// (舉例來說:於 error console)
UserException.prototype.toString = function() {
  return this.name + ': "' + this.message + '"';
}

// 創建一個物件的實例並丟出它
throw new UserException("Value too high");

try...catch 陳述

try ... catch語句標記了一組要嘗試的陳述句,並在拋出例外時指定一個或多個響應。 如果例外被拋出,try ... catch語句捕獲它。

try ... catch語句包括一個try塊,它包含一個或多個陳述句,零個或多個catch塊,包含在try塊中拋出例外時該做什麼的陳述句。 也就是說,你希望try塊成功,如果它不成功,你希望控制權傳遞給catch塊。 如果try塊內的任何語句(或在try塊內調用的函數中)拋出例外,則控制立即切換到catch塊。 如果在try塊中沒有拋出例外,則跳過catch塊。 finally塊在try和catch塊執行後執行,但在try ... catch陳述句之前的語句之前執行。

以下的範例使用try ... catch陳述句。該範例調用基於傳遞給函數的值並從陣列中檢索月份名稱的函數。如果值不對應於月份數(1-12),則會拋出一個例外,其值為“InvalidMonthNo”,並且catch塊中的陳述句將monthName變數設置為unknown。

function getMonthName(mo) {
  mo = mo - 1; // Adjust month number for array index (1 = Jan, 12 = Dec)
  var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul",
                "Aug","Sep","Oct","Nov","Dec"];
  if (months[mo]) {
    return months[mo];
  } else {
    throw "InvalidMonthNo"; //throw 關鍵字在這裏被使用
  }
}

try { // statements to try
  monthName = getMonthName(myMonth); // 函式可以丟出例外
}
catch (e) {
  monthName = "unknown";
  logMyErrors(e); // 將例外傳至例外處理機制
}

catch 區塊

你可以使用 catch 區塊來處理 try 區塊可能丟出的例外。

catch (catchID) {
  陳述句
}

catch塊指定用來保存throw語句所丟出的值的標識符(前面語法中的catchID) 您可以使用此標識符獲取有關被拋出的例外的信息。 JavaScript在進入catch塊時創建此標識符; 標識符僅持續catch塊的持續時間; 在catch塊完成執行後,標識符不再可用。

例如,下列的程式碼中丟出了一個例外,當例外發生後,控制權被轉交給 catch 區塊。

try {
  throw "myException"; // 產生例外
}
catch (e) {
  // 用於處理例外的陳述句
  logMyErrors(e); // 將例外物件傳給error handler
}

finally 區塊

finally區塊中包含 在try和catch塊執行之後但在try ... catch陳述之後的語句之前 執行的語句。 無論是否拋出例外,finally區塊都會執行。 如果拋出例外,則即使沒有catch區塊處理例外,finally塊中的陳述句也會執行。

您可以使用finally塊來使腳本在發生例外時正常地結束; 例如,您可能需要釋放腳本中綁定的資源。 在以下示例中,打開一個文件,然後執行使用該文件的陳述句(服務器端JavaScript允許您訪問文件)。 如果在打開文件時拋出例外,finally塊會在腳本結束之前關閉文件。

openMyFile();
try {
  writeMyFile(theData); //可能產生例外
} catch(e) {  
  handleError(e); //處理可能發生的例外
} finally {
  closeMyFile(); //總是在try結束後關閉檔案
}

如果finally區塊有返回值,那麼該值將成為整個try-catch-finally過程的返回值,而捨棄try和catch塊中的任何返回語句:

function f() {
  try {
    console.log(0);
    throw "bogus";
  } catch(e) {
    console.log(1);
    return true; // 這個回傳會被擱置
                 // 直到 finally 區塊結束
    console.log(2); // 不會到達這裏
  } finally {
    console.log(3);
    return false; // 覆寫先前的 "return"
    console.log(4); // 不會到達這裏
  }
  // "return false" 在這裏被執行  
  console.log(5); // 不會到達這裏
}
f(); // console 0, 1, 3; 會回傳false

finally區塊覆寫返回值也適用於在catch區塊中拋出或重新拋出的例外(即便在catch中再次丟出例外,catch所屬的finally區塊還是會被執行):

function f() {
  try {
    throw "bogus";
  } catch(e) {
    console.log('caught inner "bogus"');
    throw e; // 此處的throw陳述將被擱置到 
             // finally區塊結束
  } finally {
    return false; // 覆寫先前的"throw"
  }
  // "return false" 在此被執行
}

try {
  f();
} catch(e) {
  // 這裏永遠不可能到達因為在f函式中catch的throw
  // 被finally中的return覆寫了
  console.log('caught outer "bogus"');
}

// 輸出 -> caught inner "bogus"

巢狀 try...catch 陳述

你可以使用一個或多個的 try...catch 陳述. 假如一個內層的try...catch 陳述不具有 catch 塊, 它將必須要有finally塊與及封閉的 try...catch 陳述來檢測是否有符合的例外.

使用 Error 物件

根據錯誤的類型,您可以使用“name”和“message”屬性來獲取更精確的資訊。 'name'提供了錯誤所屬的類別(class)(例如,'DOMException'或'Error'),而'message'通常提供藉由將錯誤物件轉換為字串所獲得的更簡潔的資訊。參見 巢狀 try 區塊 位於 try...catch 參考資料頁面.

假如您要丟出自定義的例外, 為了方便使用這些屬性(例如,如果你的catch區塊並不要區分你自己的例外和系統的),你可以使用Error構造子。舉例來說:

function doSomethingErrorProne () {
  if (ourCodeMakesAMistake()) {
    throw (new Error('The message'));
  } else {
    doSomethingToGetAJavascriptError();
  }
}
....
try {
  doSomethingErrorProne();
}
catch (e) {
  console.log(e.name); // 紀錄 'Error'
  console.log(e.message); // 紀錄 'The message' 或者其他 JavaScript 例外的資訊)
}

Promises容器

從 ECMAScript2015 起, JavaScript 支援 Promise 物件,允許您控制延遲和異步操作的流程。

Promise 有以下幾種狀態:

  • 等待: 初始狀態,不是滿足或拒絕.
  • 滿足: 操作成功.
  • 拒絕: 操作失敗.
  • 安定: 以上皆非的一種狀態.

使用XHR載入圖檔

這裏有個簡單的範例,使用了 Promise物件 與及 XMLHttpRequest 來載入 MDN GitHub promise-test repository中的一張圖檔. 你也可以觀看結果. 每一步都有註解來讓您慢慢理解 Promise物件與及XHR架構. 下面的版本沒有註解,但藉由觀察 Promise 物件的變動您或許可以對promise物件有所遼解:

function imgLoad(url) {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'blob';
    request.onload = function() {
      if (request.status === 200) {
        resolve(request.response);
      } else {
        reject(Error('Image didn\'t load successfully; error code:' 
                     + request.statusText));
      }
    };
    request.onerror = function() {
      reject(Error('There was a network error.'));
    };
    request.send();
  });
}

以獲得更多資訊,查看 Promise 參照頁面.

文件標籤與貢獻者

 此頁面的貢獻者: iigmir, Kaiyeh, jackblackevo
 最近更新: iigmir,