ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 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'}); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **练习:`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' ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **练习:`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)。 ![](https://img.kancloud.cn/ff/a8/ffa8e16628cad59b09c786b836722faa.svg) **测验** 参见[测验应用程序](ch_quizzes-exercises.html#quizzes)。