## 21.控制流語句
> 原文: [http://exploringjs.com/impatient-js/ch_control-flow.html](http://exploringjs.com/impatient-js/ch_control-flow.html)
>
> 貢獻者:[飛龍](https://github.com/wizardforcel)
本章介紹以下控制流語句:
* `if`語句(ES1)
* `switch`語句(ES3)
* `while`循環(ES1)
* `do-while`循環(ES3)
* `for`循環(ES1)
* `for-of`循環(ES6)
* `for-await-of`循環(ES2018)
* `for-in`循環(ES1)
在我們得到實際的控制流語句之前,讓我們看看兩個用于控制循環的運算符。
### 21.1。控制循環:`break`和`continue`
當您在循環中時,兩個運算符`break`和`continue`可用于控制循環和其他語句。
#### 21.1.1。 `break`
`break`有兩個版本:一個帶有操作數,另一個沒有操作數。后一版本在以下語句中起作用:`while`,`do-while`,`for`,`for-of`,`for-await-of`,`for-in`和`switch`。它立即離開當前的語句:
```js
for (const x of ['a', 'b', 'c']) {
console.log(x);
if (x === 'b') break;
console.log('---')
}
// Output:
// 'a'
// '---'
// 'b'
```
#### 21.1.2。 `break`的附加用例:離開塊
帶有操作數的`break`隨處可見。其操作數是*標簽*。標簽可以放在任何語句之前,包括塊。 `break foo`離開標簽為`foo`的語句:
```js
foo: { // label
if (condition) break foo; // labeled break
// ···
}
```
如果您使用循環并希望區分找到您要查找的內容并完成循環,以及沒有成功,則從塊中斷會偶爾會很方便:
```js
function search(stringArray, suffix) {
let result;
search_block: {
for (const str of stringArray) {
if (str.endsWith(suffix)) {
// Success
result = str;
break search_block;
}
} // for
// Failure
result = '(Untitled)';
} // search_block
return { suffix, result };
// same as: {suffix: suffix, result: result}
}
assert.deepEqual(
search(['foo.txt', 'bar.html'], '.html'),
{ suffix: '.html', result: 'bar.html' }
);
assert.deepEqual(
search(['foo.txt', 'bar.html'], '.js'),
{ suffix: '.js', result: '(Untitled)' }
);
```
#### 21.1.3。 `continue`
`continue`僅適用于`while`,`do-while`,`for`,`for-of`,`for-await-of`和`for-in`。它立即離開當前循環并繼續下一個循環。例如:
```js
const lines = [
'Normal line',
'# Comment',
'Another normal line',
];
for (const line of lines) {
if (line.startsWith('#')) continue;
console.log(line);
}
// Output:
// 'Normal line'
// 'Another normal line'
```
### 21.2。 `if`語句
這是兩個簡單的`if`語句:一個只有一個`then`分支,一個帶有`then`分支和一個`else`分支:
```js
if (cond) {
// then branch
}
if (cond) {
// then branch
} else {
// else branch
}
```
`else`也可以跟隨另一個`if`語句,而不是塊:
```js
if (cond1) {
// ···
} else if (cond2) {
// ···
}
if (cond1) {
// ···
} else if (cond2) {
// ···
} else {
// ···
}
```
您可以使用更多`else if`來繼續這個鏈條。
#### 21.2.1。 `if`語句的語法
`if`語句的一般語法是:
```js
if (cond) ?then_statement?
else ?else_statement?
```
到目前為止,`then_statement`一直是一個塊,但你也可以使用一個語句。該語句必須以分號結束:
```js
if (true) console.log('Yes'); else console.log('No');
```
這意味著`else if`不是獨立的構造,它只是一個`if`語句,其`else_statement`是另一個`if`語句。
### 21.3。 `switch`語句
`switch`語句的頭部如下所示:
```js
switch (?switch_expression?) {
?switch_body?
}
```
在`switch`的正文內部,有零個或多個 case 子句:
```js
case ?case_expression?:
?statements?
```
并且默認子句是可選的:
```js
default:
?statements?
```
`switch`執行如下:
* 求值`switch`表達式。
* 跳轉到第一個`case`子句,其表達式與`switch`表達式結果相同。
* 如果沒有這樣的`case`子句,請跳轉到`default`子句。
* 如果沒有默認子句,則不會發生任何事情。
#### 21.3.1。第一個例子
讓我們看一個例子:以下函數將一個數字從 1-7 轉換為工作日的名稱。
```js
function dayOfTheWeek(num) {
switch (num) {
case 1:
return 'Monday';
case 2:
return 'Tuesday';
case 3:
return 'Wednesday';
case 4:
return 'Thursday';
case 5:
return 'Friday';
case 6:
return 'Saturday';
case 7:
return 'Sunday';
}
}
assert.equal(dayOfTheWeek(5), 'Friday');
```
#### 21.3.2。別忘了`return`或`break`!
在 case 子句的末尾,繼續執行下一個`case`子句(除非你`return`或`break`)。例如:
```js
function dayOfTheWeek(num) {
let name;
switch (num) {
case 1:
name = 'Monday';
case 2:
name = 'Tuesday';
case 3:
name = 'Wednesday';
case 4:
name = 'Thursday';
case 5:
name = 'Friday';
case 6:
name = 'Saturday';
case 7:
name = 'Sunday';
}
return name;
}
assert.equal(dayOfTheWeek(5), 'Sunday'); // not 'Friday'!
```
也就是說,`dayOfTheWeek()`的先前實現起了作用,因為我們使用了`return`。我們可以使用`break`修復此實現:
```js
function dayOfTheWeek(num) {
let name;
switch (num) {
case 1:
name = 'Monday';
break;
case 2:
name = 'Tuesday';
break;
case 3:
name = 'Wednesday';
break;
case 4:
name = 'Thursday';
break;
case 5:
name = 'Friday';
break;
case 6:
name = 'Saturday';
break;
case 7:
name = 'Sunday';
break;
}
return name;
}
assert.equal(dayOfTheWeek(5), 'Friday');
```
#### 21.3.3。空`case`子句
可以省略`case`子句的語句,這有效地為每個`case`子句提供了多個`case`表達式:
```js
function isWeekDay(name) {
switch (name) {
case 'Monday':
case 'Tuesday':
case 'Wednesday':
case 'Thursday':
case 'Friday':
return true;
case 'Saturday':
case 'Sunday':
return false;
}
}
assert.equal(isWeekDay('Wednesday'), true);
assert.equal(isWeekDay('Sunday'), false);
```
#### 21.3.4。通過`default`子句檢查非法值
如果`switch`表達式沒有其他匹配項,則跳轉到`default`子句。這使它對錯誤檢查很有用:
```js
function isWeekDay(name) {
switch (name) {
case 'Monday':
case 'Tuesday':
case 'Wednesday':
case 'Thursday':
case 'Friday':
return true;
case 'Saturday':
case 'Sunday':
return false;
default:
throw new Error('Illegal value: '+name);
}
}
assert.throws(
() => isWeekDay('January'),
{message: 'Illegal value: January'});
```
 **練習:`switch`**
* `exercises/control-flow/number_to_month_test.js`
* 獎金:`exercises/control-flow/is_object_via_switch_test.js`
### 21.4。 `while`循環
`while`循環具有以下語法:
```js
while (?condition?) {
?statements?
}
```
在每次循環之前,`while`求值`condition`:
* 如果結果是假值,則循環結束。
* 如果結果是真值,則`while`主體再次執行。
#### 21.4.1。例子
以下代碼使用`while`循環。在每次循環中,它通過`.shift()`刪除`arr`的第一個元素并記錄它。
```js
const arr = ['a', 'b', 'c'];
while (arr.length > 0) {
const elem = arr.shift(); // remove first element
console.log(elem);
}
// Output:
// 'a'
// 'b'
// 'c'
```
如果條件是`true`,則`while`是無限循環:
```js
while (true) {
if (Math.random() === 0) break;
}
```
### 21.5。 `do-while`循環
`do-while`循環的工作原理與`while`非常相似,但它會在每次循環之后(之前)檢查其條件。
```js
let input;
do {
input = prompt('Enter text:');
} while (input !== ':q');
```
### 21.6。 `for`循環
對于`for`循環,您可以使用頭部控制其主體的執行方式。頭部有三個部分,每個部分都是可選的:
```js
for (?initialization?; ?condition?; ?post_iteration?) {
?statements?
}
```
* `initialization`:為循環設置變量等。此處通過`let`或`const`語句的變量僅存在于循環內。
* `condition`:在每次循環之前檢查此條件。如果是假值,循環就會停止。
* `post_iteration`:此代碼在每次循環之后執行。
因此,`for`循環大致等同于以下`while`循環:
```js
?initialization?
while (?condition?) {
?statements?
?post_iteration?
}
```
#### 21.6.1。例子
例如,這是如何通過`for`循環從零計數到二:
```js
for (let i=0; i<3; i++) {
console.log(i);
}
// Output:
// 0
// 1
// 2
```
這是通過`for`循環記錄數組內容的方法:
```js
const arr = ['a', 'b', 'c'];
for (let i=0; i<3; i++) {
console.log(arr[i]);
}
// Output:
// 'a'
// 'b'
// 'c'
```
如果省略頭部的所有三個部分,則會得到無限循環:
```js
for (;;) {
if (Math.random() === 0) break;
}
```
### 21.7。 `for-of`循環
`for-of`循環遍歷*可迭代對象* - 一個支持[迭代協議](ch_sync-iteration.html)的數據容器。每個迭代值都存儲在一個變量中,在頭部中指定:
```js
for (?iteration_variable? of ?iterable?) {
?statements?
}
```
迭代變量通常通過變量語句創建:
```js
const iterable = ['hello', 'world'];
for (const elem of iterable) {
console.log(elem);
}
// Output:
// 'hello'
// 'world'
```
但是您也可以使用已存在的(可變)變量:
```js
const iterable = ['hello', 'world'];
let elem;
for (elem of iterable) {
console.log(elem);
}
```
#### 21.7.1。 `const`:`for-of`與`for`
請注意,在`for-of`循環中,您可以使用`const`。迭代變量對于每次迭代仍然可以是不同的(它在迭代期間不能改變)。將其視為每個迭代的新`const`語句,在新的作用域內。
相反,在`for`循環中,如果它們的值發生變化,則必須通過`let`或`var`語句變量。
#### 21.7.2。可迭代對象的迭代
如前所述,`for-of`適用于任何可迭代對象,而不僅僅是數組。例如,使用集合:
```js
const set = new Set(['hello', 'world']);
for (const elem of set) {
console.log(elem);
}
```
#### 21.7.3。迭代`[index, element]`對數組
最后,您還可以使用`for-of`迭代數組的`[index, element]`條目:
```js
const arr = ['a', 'b', 'c'];
for (const [index, elem] of arr.entries()) {
console.log(`${index} -> ${elem}`);
}
// Output:
// '0 -> a'
// '1 -> b'
// '2 -> c'
```
 **練習:`for-of`**
`exercises/control-flow/array_to_string_test.js`
### 21.8。 `for-await-of`循環
`for-await-of`與`for-of`類似,但它適用于異步迭代而不是同步迭代。它只能在異步函數和異步生成器中使用。
```js
for await (const item of asyncIterable) {
// ···
}
```
`for-await-of`將在后面的章節中詳細描述。
### 21.9。 `for-in`循環(避免)
`for-in`有幾個陷阱。因此,通常最好避免它。
這是使用`for-in`的一個例子:
```js
function getOwnPropertyNames(obj) {
const result = [];
for (const key in obj) {
if ({}.hasOwnProperty.call(obj, key)) {
result.push(key);
}
}
return result;
}
assert.deepEqual(
getOwnPropertyNames({ a: 1, b:2 }),
['a', 'b']);
assert.deepEqual(
getOwnPropertyNames(['a', 'b']),
['0', '1']); // strings!
```
這是一個更好的選擇:
```js
function getOwnPropertyNames(obj) {
const result = [];
for (const key of Object.keys(obj)) {
result.push(key);
}
return result;
}
```
有關`for-in`的更多信息,請參閱[“Speaking JavaScript”](http://speakingjs.com/es5/ch13.html#for-in)。
 **測驗**
參見[測驗應用程序](ch_quizzes-exercises.html#quizzes)。
- I.背景
- 1.關于本書(ES2019 版)
- 2.常見問題:本書
- 3. JavaScript 的歷史和演變
- 4.常見問題:JavaScript
- II.第一步
- 5.概覽
- 6.語法
- 7.在控制臺上打印信息(console.*)
- 8.斷言 API
- 9.測驗和練習入門
- III.變量和值
- 10.變量和賦值
- 11.值
- 12.運算符
- IV.原始值
- 13.非值undefined和null
- 14.布爾值
- 15.數字
- 16. Math
- 17. Unicode - 簡要介紹(高級)
- 18.字符串
- 19.使用模板字面值和標記模板
- 20.符號
- V.控制流和數據流
- 21.控制流語句
- 22.異常處理
- 23.可調用值
- VI.模塊化
- 24.模塊
- 25.單個對象
- 26.原型鏈和類
- 七.集合
- 27.同步迭代
- 28.數組(Array)
- 29.類型化數組:處理二進制數據(高級)
- 30.映射(Map)
- 31. WeakMaps(WeakMap)
- 32.集(Set)
- 33. WeakSets(WeakSet)
- 34.解構
- 35.同步生成器(高級)
- 八.異步
- 36. JavaScript 中的異步編程
- 37.異步編程的 Promise
- 38.異步函數
- IX.更多標準庫
- 39.正則表達式(RegExp)
- 40.日期(Date)
- 41.創建和解析 JSON(JSON)
- 42.其余章節在哪里?