1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
|
---
title: 流程控制與例外處理
slug: Web/JavaScript/Guide/Control_flow_and_error_handling
tags:
- Beginner
- Guide
- JavaScript
- 初學者
- 指南
translation_of: Web/JavaScript/Guide/Control_flow_and_error_handling
---
<div>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Grammar_and_types", "Web/JavaScript/Guide/Loops_and_iteration")}}</div>
<p>JavaScript 擁有許多陳述式,特別是流程控制的陳述式,你可以用這些陳述式來增加程式的互動性。這個章節將會概要介紹陳述式。</p>
<p><a href="/zh-TW/docs/Web/JavaScript/Reference/Statements">JavaScript 參考</a>中有比本章更多關於陳述式的細節。 在 Javascript 程式碼中,分號(;)被用來隔開陳述式。</p>
<p>任何 JavaScript 運算式也是一個陳述式。 有關運算式的完整資訊,請參閱<a href="/zh-TW/docs/Web/JavaScript/Guide/Expressions_and_Operators">運算式與運算子</a>。</p>
<h2 id="區塊陳述式">區塊陳述式</h2>
<p>最基本的陳述式是「用於將陳述式分塊」的「區塊陳述式」。程式區塊被以一對大括號隔離開來:</p>
<pre class="syntaxbox">{
陳述式 1;
陳述式 2;
.
.
.
陳述式 n;
}
</pre>
<h3 id="範例"><strong>範例</strong></h3>
<p>區塊陳述式經常與流程控制陳述式(例如:<code>if</code>、<code>for</code>、<code>while</code>)搭配使用。</p>
<pre class="brush: js">while (x < 10) {
x++;
}
</pre>
<p>在這裏,<code>{ x++; }</code> 是區塊陳述式。</p>
<p><strong>重要</strong>:JavaScript 在 ECMAScript2015 (第六版)以前的版本並<strong>沒有</strong>區塊範圍的概念。 在區塊中的變數有效範圍是包含該區塊的函式或者是執行檔,並且在區塊外也可使用它們。換句話說,區塊並不定義了範圍。JavaScript 中的"獨立"區塊將會產生與 C 和 Java 中不同的效果。舉例來說:</p>
<pre class="brush: js">var x = 1;
{
var x = 2;
}
console.log(x); // 輸出 2
</pre>
<p>這裏輸出了 2 是因為區塊中變數 <code>var x</code> 陳述式擁有與區塊外的 <code>var x</code> 相同的有效範圍。在 C 或 Java,相同的程式碼將會輸出 1。</p>
<p>從 ECMAScript2015 版本起, 使用 <code>let</code> 定義的變數範圍將被限制於區塊內。</p>
<h2 id="條件陳述式">條件陳述式</h2>
<p>條件陳述式是一些在指定條件爲真下將被執行的指令。 JavaScript 支援兩種條件陳述式: <code>if...else</code> 和 <code>switch</code>。</p>
<h3 id="if...else_陳述式"><code>if...else</code> 陳述式</h3>
<p><code>if 陳述式將在「邏輯判斷爲真」下執行接下來的一個陳述式</code>。選擇性的 <code>else</code> 陳述式將會在「邏輯判斷爲否」時被執行。 <code>if</code> 陳述式的用法看起來如下:</p>
<pre class="syntaxbox">if (指定條件) {
陳述式 1;
} else {
陳述式 2;
}</pre>
<p>指定條件可以是任何會回傳true或false的運算式。參見 <a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Boolean#Description">Boolean</a> 來進一步瞭解哪些運算式會回傳 <code>true</code> 及 <code>false</code>。假如指定條件爲 <code>true</code>,陳述式 1 會被執行;否則,陳述式 2 會被執行。陳述式 1 及陳述式 2 可以是任何陳述式,包含巢狀 <code>if</code> 陳述式。</p>
<p>你也可以藉由 <code>else if</code> 來使用複合的陳述式來測試多種不同的條件,如下:</p>
<pre class="syntaxbox">if (指定條件1) {
陳述式 1;
} else if (指定條件 2) {
陳述式 2;
} else if (指定條件 n) {
陳述式 n;
} else {
最後陳述式;
}
</pre>
<p>在多個條件中,只有第一個條件爲 true 的陳述式會被執行。若要執行多個陳述式,可以將陳述式包在同一個程式區塊中(<code>{ ... }</code>)。 通常來說,使用區塊陳述式是很好的習慣,尤其在使用巢狀 if 的時候:</p>
<pre class="syntaxbox">if (指定條件) {
陳述式_1_執行_當_指定條件_爲_真;
陳述式_2_執行_當_指定條件_爲_真;
} else {
陳述式_3_執行_當_指定條件_爲_否;
陳述式_4_執行_當_指定條件_爲_否;
}
</pre>
<div>建議不要在以賦值作爲條件運算式,因為"賦值"常常被和"等於"搞混。 例如, 不要寫出如下面的程式碼:</div>
<pre class="example-bad brush: js">if (x = y) {
/* 陳述式 */
}
</pre>
<p>如果你真的需要以賦值作爲條件運算式,一種常見的方式是額外在賦值式外面加上一組括號。例如:</p>
<pre class="brush: js">if ((x = y)) {
/* 陳述式 */
}
</pre>
<h4 id="爲否的值">爲"否"的值</h4>
<p>下列的值會被看作 false(也稱為 {{Glossary("Falsy")}} 值)</p>
<ul>
<li><code>false</code></li>
<li><code>undefined</code></li>
<li><code>null</code></li>
<li><code>0</code></li>
<li><code>NaN</code></li>
<li>空字串(<code>""</code>)</li>
</ul>
<p>其他所有的值,包含所有物件,當被作為條件陳述式都會被視為 <code>true</code>。</p>
<p>不要把"布林真假值"和"布林物件的真假值"弄混了。 例如:</p>
<pre class="brush: js">var b = new Boolean(false);
if (b) // 這會是 True
if (b == true) // 這會是 false
</pre>
<h4 id="範例_2"><strong>範例</strong></h4>
<p>在下面的範例中,函式 <code>checkData</code> 回傳 <code>true</code> 當 <code>Text</code> 物件的長度爲三;否則, 顯示出 alert 並回傳 <code>false</code>。</p>
<pre class="brush: js">function checkData() {
if (document.form1.threeChar.value.length == 3) {
return true;
} else {
alert("請輸入恰好三個字元. " +
document.form1.threeChar.value + " is not valid.");
return false;
}
}
</pre>
<h3 id="switch_陳述式"><code>switch</code> 陳述式</h3>
<p><code>switch</code> 陳述式允許程式依運算式的不同值來選擇不同標籤。 假如運算式和標籤匹配,程式會執行標籤對應的陳述式。範例如下:</p>
<pre class="syntaxbox">switch (運算式) {
case 標籤 1:
陳述式 1
[break;]
case 標籤 2:
陳述式 2
[break;]
...
default:
陳述式
[break;]
}
</pre>
<p>程序首先尋找一個標籤與運算式的值匹配的 <code>case</code> 子句,然後將控制權轉移給該子句,執行與其相關的陳述式。 如果沒有找到匹配的標籤,程序將查找 <code>default</code> 子句(選擇性),如果找到,則將控制權轉移到該子句,執行關聯的陳述式。 如果沒有找到 <code>default</code> 子句,程序繼續在 <code>switch</code> 結束後的陳述式執行。 按照慣例,默認子句是最後一個子句,但並不硬性規定。</p>
<p>與每個 <code>case</code> 子句相關聯的 <code>break</code> 陳述式(選擇性)確保程序在發現匹配的陳述式之後退出 <code>switch</code>,並在 <code>switch</code> 後的陳述式中繼續執行。 如果省略 <code>break</code>,程序將繼續在 <code>switch</code> 陳述式中的下一個陳述式執行。</p>
<h4 id="範例_3"><strong>範例</strong></h4>
<p>在下面範例中,如果變數 <code>fruittype</code> 為「Bananas」,程序將與「Bananas」匹配並執行相關陳述式。 當遇到 <code>break</code> 時,程序離開 <code>switch</code> 並執行 <code>switch</code> 後的陳述式。 如果省略 <code>break</code>,也將執行 case 「Cherries」的陳述式。</p>
<pre class="brush: js">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?");</pre>
<h2 id="例外處理陳述式">例外處理陳述式</h2>
<p>你可以用以 <code>throw</code> 陳述式丟出例外,並以 <code>try...catch</code> 陳述式處理之。</p>
<ul>
<li><a href="#throw_statement"><code>throw</code> 陳述式</a></li>
<li><a href="#try...catch_statement"><code>try...catch</code> 陳述式</a></li>
</ul>
<h3 id="例外的形態">例外的形態</h3>
<p>任何物件(object)都可以在 JavaScript 中被拋出。 然而,並非所有拋出的物件都相同。 雖然將數字或字串作為錯誤物件使用是相當常見的,但使用為此目的專門創造的一種例外物件類型通常更有效:</p>
<ul>
<li><a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects#Fundamental_objects">ECMAScript 例外</a></li>
<li>{{domxref("DOMException")}} and {{domxref("DOMError")}}</li>
</ul>
<h3 id="throw_陳述式"><code>throw</code> 陳述式</h3>
<p>使用 <code>throw</code> 陳述式拋出例外。當拋出例外時,你要指定包含在要拋出物件中的值:</p>
<pre class="syntaxbox">throw expression;
</pre>
<p>您可以拋出任何運算式,而不僅僅是特定類型的運算式。以下的程式碼會拋出一些不同類型的例外:</p>
<pre class="brush: js">throw "Error2"; // 字串形態
throw 42; // 數字形態
throw true; // True/False
throw {toString: function() { return "我是物件!"; } };
</pre>
<div class="notecard note">
<p><strong>備註:</strong>您可以在拋出例外時指定物件。 然後,可以在 catch 區塊中引用對象的屬性。</p>
</div>
<pre class="brush: js">// 創建類型爲 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");</pre>
<h3 id="try...catch_陳述式"><code>try...catch</code> 陳述式</h3>
<p><code>try...catch</code> 陳述式標記了一組要嘗試的陳述式,並在拋出例外時指定一個或多個響應。 如果例外被拋出,<code>try...catch</code> 陳述式捕獲它。</p>
<p><code>try...catch</code> 陳述式包括一個 <code>try</code> 區塊,它包含一個或多個陳述式,零個或多個 <code>catch</code> 區塊,包含在 <code>try</code> 區塊中拋出例外時該做什麼的陳述式。 也就是說,你希望 <code>try</code> 區塊成功,如果它不成功,你希望控制權傳遞給 <code>catch</code> 區塊。 如果 <code>try</code> 區塊內的任何陳述式(或在 <code>try</code> 區塊內調用的函數中)拋出例外,則控制立即切換到 <code>catch</code> 區塊。 如果在 <code>try</code> 區塊中沒有拋出例外,則跳過 <code>catch</code> 區塊。 <code>finally</code> 區塊在 <code>try</code> 和 <code>catch</code> 區塊執行後執行,但在 <code>try...catch</code> 陳述式之後的陳述式之前執行。</p>
<p>以下的範例使用 <code>try...catch</code> 陳述式。該範例調用基於傳遞給函數的值並從陣列中檢索月份名稱的函數。如果值不對應於月份數(1-12),則會拋出一個例外,其值為 "InvalidMonthNo",並且 <code>catch</code> 區塊中的陳述式將 <code>monthName</code> 變數設置為 <code>unknown</code>。</p>
<pre class="brush: js">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); // 將例外傳至例外處理機制
}
</pre>
<h4 id="The_catch_Block"><code>catch</code> 區塊</h4>
<p>你可以使用 <code>catch</code> 區塊來處理 <code>try</code> 區塊可能丟出的例外。</p>
<pre class="syntaxbox">catch (catchID) {
陳述式
}
</pre>
<p><code>catch</code> 區塊指定用來保存 <code>throw</code> 陳述式所丟出的值的標識符(前面語法中的 <code>catchID</code>) 您可以使用此標識符獲取有關被拋出的例外的信息。 JavaScript 在進入<code>catch</code> 區塊時創建此標識符; 標識符僅持續 <code>catch</code> 區塊的持續時間;在 <code>catch</code> 區塊完成執行後,標識符不再可用。</p>
<p>例如,下列的程式碼中丟出了一個例外,當例外發生後,控制權被轉交給 <code>catch</code> 區塊。</p>
<pre class="brush: js">try {
throw "myException"; // 產生例外
}
catch (e) {
// 用於處理例外的陳述式
logMyErrors(e); // 將例外物件傳給 error handler
}
</pre>
<h4 id="finally_區塊"><code>finally</code> 區塊</h4>
<p><code>finally</code> 區塊中包含在 <code>try</code> 和 <code>catch</code> 區塊執行之後但在 <code>try...catch</code> 陳述式之後的陳述式之前 執行的陳述式。 無論是否拋出例外,<code>finally</code> 區塊都會執行。 如果拋出例外,則即使沒有 <code>catch</code> 區塊處理例外,<code>finally</code> 區塊中的陳述式也會執行。</p>
<p>您可以使用 <code>finally</code> 區塊來使腳本在發生例外時正常地結束。例如,您可能需要釋放腳本中綁定的資源。 在以下示例中,打開一個文件,然後執行使用該文件的陳述式(伺服器端 JavaScript 允許您訪問文件)。 如果在打開文件時拋出例外,<code>finally</code> 區塊會在腳本結束之前關閉文件。</p>
<pre class="brush: js">openMyFile();
try {
writeMyFile(theData); // 可能產生例外
} catch(e) {
handleError(e); // 處理可能發生的例外
} finally {
closeMyFile(); // 總是在 try 結束後關閉檔案
}
</pre>
<p>如果 <code>finally</code> 區塊有返回值,那麼該值將成為整個 <code>try-catch-finally</code> 過程的返回值,而捨棄 <code>try</code> 和 <code>catch</code> 區塊中的任何返回陳述式:</p>
<pre class="brush: js">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
</pre>
<p><code>finally</code> 區塊覆寫返回值也適用於在 <code>catch</code> 區塊中拋出或重新拋出的例外(即便在<code>catch</code> 中再次丟出例外,<code>catch</code> 所屬的 <code>finally</code> 區塊還是會被執行):</p>
<pre class="brush: js">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"</pre>
<h4 id="Nesting_try...catch_Statements">巢狀 try...catch 陳述式</h4>
<p>你可以使用一個或多個的 <code>try...catch</code> 陳述式。 假如一個內層的<code>try...catch</code> 陳述式不具有 <code>catch</code> 區塊, 它將必須要有 <code>finally</code> 區塊與及封閉的 <code>try...catch</code> 陳述式來檢測是否有符合的例外。</p>
<h3 id="使用_Error_物件">使用 <code>Error</code> 物件</h3>
<p>根據錯誤的類型,您可以使用 "name" 和 "message" 屬性來獲取更精確的資訊。"name" 提供了錯誤所屬的類別(class)(例如,"DOMException" 或 "Error"),而 "message" 通常提供藉由將錯誤物件轉換為字串所獲得的更簡潔的資訊。參見<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#Nested_try-blocks">巢狀 try 區塊</a>位於 <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try...catch</a></code> 參考資料頁面。</p>
<p>假如您要丟出自定義的例外, 為了方便使用這些屬性(例如,如果你的 <code>catch</code> 區塊並不要區分你自己的例外和系統的),你可以使用 <code>Error</code> 構造子。舉例來說:</p>
<pre class="brush: js">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 例外的資訊)
}</pre>
<h2 id="Promises_容器">Promises 容器</h2>
<p>從 ECMAScript2015 起,JavaScript 支援 {{jsxref("Promise")}} 物件,允許您控制延遲和異步操作的流程。</p>
<p><code>Promise</code> 有以下幾種狀態:</p>
<ul>
<li><em>pending</em>:等待中,為初始之狀態,即不是 fulfilled 也不是 rejected。</li>
<li><em>fulfilled</em>:已實現,表示操作成功完成。</li>
<li><em>rejected</em>:已拒絕,表示操作失敗。</li>
<li><em>settled</em>:已完成,表示 Promise 狀態為已實現或已拒絕,但不是等待中。</li>
</ul>
<p><img alt="promises" src="https://mdn.mozillademos.org/files/8633/promises.png"></p>
<h3 id="使用_XHR_載入圖檔">使用 XHR 載入圖檔</h3>
<p>這裏有個簡單的範例,使用了 <code>Promise</code> 物件與及 <code><a href="/zh-TW/docs/Web/API/XMLHttpRequest">XMLHttpRequest</a></code> 來載入 MDN GitHub<a href="https://github.com/mdn/promises-test/blob/gh-pages/index.html"> promise-test</a> repository 中的一張圖檔。你也可以<a href="http://mdn.github.io/promises-test/">觀看結果</a>。 每一步都有註解來讓您慢慢理解 Promise 物件與及 XHR 架構。 下面的版本沒有註解,但藉由觀察 <code>Promise</code> 物件的變動您或許可以對 promise 物件有所了解:</p>
<pre class="brush: js">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();
});
}</pre>
<p>以獲得更多資訊,查看 {{jsxref("Promise")}} 參照頁面,以及<a href="/zh-TW/docs/Web/JavaScript/Guide/Using_promises">使用 Promises</a> 教學。</p>
<div>{{PreviousNext("Web/JavaScript/Guide/Grammar_and_types", "Web/JavaScript/Guide/Loops_and_iteration")}}</div>
|