原文: http://exploringjs.com/impatient-js/ch_control-flow.html
本章介绍以下控制流语句:
if语句(ES1)switch语句(ES3)while循环(ES1)do-while循环(ES3)for循环(ES1)for-of循环(ES6)for-await-of循环(ES2018)for-in循环(ES1)在我们得到实际的控制流语句之前,让我们看看两个用于控制循环的运算符。
break和continue当您在其中时,两个运算符break和continue可用于控制循环和其他语句。
breakbreak有两个版本:一个带有操作数,另一个没有操作数。后一版本在以下语句中起作用:while,do-while,for,for-of,for-await-of,for-in和switch。它立即离开了当前的声明:
for (const x of ['a', 'b', 'c']) {
console.log(x);
if (x === 'b') break;
console.log('---')
}
// Output:
// 'a'
// '---'
// 'b'
break的附加用例:留下块带有操作数的break随处可见。其操作数是 _ 标签 _。标签可以放在任何声明之前,包括块。 break foo留下标签为foo的语句:
foo: { // label
if (condition) break foo; // labeled break
// ···
}
如果您使用循环并希望区分找到您要查找的内容并完成循环而没有成功,则从块中断会偶尔会很方便:
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)' }
);
continuecontinue仅适用于while,do-while,for,for-of,for-await-of和for-in。它立即离开当前循环迭代并继续下一个循环。例如:
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'
if语句这是两个简单的if语句:一个只有一个“then”分支,一个带有“then”分支和一个“else”分支:
if (cond) {
// then branch
}
if (cond) {
// then branch
} else {
// else branch
}
而不是块,else也可以跟随另一个if语句:
if (cond1) {
// ···
} else if (cond2) {
// ···
}
if (cond1) {
// ···
} else if (cond2) {
// ···
} else {
// ···
}
您可以使用更多else if来继续此链。
if语句的语法if语句的一般语法是:
if (cond) «then_statement»
else «else_statement»
到目前为止,then_statement一直是一个块,但你也可以使用一个声明。该声明必须以分号结束:
if (true) console.log('Yes'); else console.log('No');
这意味着else if不是它自己的构造,它只是一个if语句,其else_statement是另一个if语句。
switch声明switch语句的头部如下所示:
switch («switch_expression») {
«switch_body»
}
在switch的主体内部,有零个或多个 case 子句:
case «case_expression»:
«statements»
并且,可选地,默认子句:
default:
«statements»
switch执行如下:
让我们看一个例子:以下函数将一个数字从 1-7 转换为工作日的名称。
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');
return或break!在 case 子句的末尾,继续执行下一个 case 子句(除非你return或break)。例如:
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修复此实现:
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');
可以省略 case 子句的语句,这有效地为每个 case 子句提供了多个 case 表达式:
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);
default子句检查非法值如果switch表达式没有其他匹配,则跳转到default子句。这使它对错误检查很有用:
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'});
exercises/control-flow/number_to_month_test.js
奖金:exercises/control-flow/is_object_via_switch_test.js
while循环while循环具有以下语法:
while («condition») {
«statements»
}
在每次循环迭代之前,while评估condition:
while主体再次执行。以下代码使用while循环。在每次循环迭代中,它通过.shift()删除arr的第一个元素并记录它。
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是无限循环:
while (true) {
if (Math.random() === 0) break;
}
do-while循环do-while循环的工作原理与while非常相似,但它会在每次循环迭代后(之前)检查其条件 。
let input;
do {
input = prompt('Enter text:');
} while (input !== ':q');
for循环使用for循环,您可以使用头控制其主体的执行方式。头部有三个部分,每个部分都是可选的:
for («initialization»; «condition»; «post_iteration») {
«statements»
}
initialization:为循环设置变量等。此处通过let或const声明的变量仅存在于循环内。condition:在每次循环迭代之前检查此条件。如果是假的,循环就会停止。post_iteration:此代码在每次循环迭代后执行。因此,for循环大致等同于以下while循环:
«initialization»
while («condition») {
«statements»
«post_iteration»
}
例如,这是如何通过for循环从零计数到两个:
for (let i=0; i<3; i++) {
console.log(i);
}
// Output:
// 0
// 1
// 2
这是通过for循环记录数组内容的方法:
const arr = ['a', 'b', 'c'];
for (let i=0; i<3; i++) {
console.log(arr[i]);
}
// Output:
// 'a'
// 'b'
// 'c'
如果省略头部的所有三个部分,则会得到无限循环:
for (;;) {
if (Math.random() === 0) break;
}
for-of循环for-of循环遍历 _ 可迭代 _ - 一个支持迭代协议的数据容器。每个迭代值都存储在一个变量中,如 head 中所指定:
for («iteration_variable» of «iterable») {
«statements»
}
迭代变量通常通过变量声明创建:
const iterable = ['hello', 'world'];
for (const elem of iterable) {
console.log(elem);
}
// Output:
// 'hello'
// 'world'
但是您也可以使用已存在的(可变)变量:
const iterable = ['hello', 'world'];
let elem;
for (elem of iterable) {
console.log(elem);
}
const:for-of与for请注意,在for-of循环中,您可以使用const。迭代变量对于每次迭代仍然可以是不同的(它在迭代期间不能改变)。将其视为每次执行的新const声明,在新的范围内。
相反,在for循环中,如果它们的值发生变化,则必须通过let或var声明变量。
如前所述,for-of适用于任何可迭代对象,而不仅仅是 Arrays。例如,使用集合:
const set = new Set(['hello', 'world']);
for (const elem of set) {
console.log(elem);
}
最后,您还可以使用for-of迭代 Arrays 的[index,element]条目:
const arr = ['a', 'b', 'c'];
for (const [index, elem] of arr.entries()) {
console.log(`${index} -> ${elem}`);
}
// Output:
// '0 -> a'
// '1 -> b'
// '2 -> c'
exercises/control-flow/array_to_string_test.js
for-await-of循环for-await-of与for-of类似,但它适用于异步迭代而不是同步迭代。它只能在异步函数和异步生成器中使用。
for await (const item of asyncIterable) {
// ···
}
for-await-of将在后面的章节中详细描述。
for-in循环(避免)for-in有几个陷阱。因此,通常最好避免它。
这是使用for-in的一个例子:
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!
这是一个更好的选择:
function getOwnPropertyNames(obj) {
const result = [];
for (const key of Object.keys(obj)) {
result.push(key);
}
return result;
}
有关for-in的更多信息,请参阅“Speaking JavaScript”。
参见测验应用程序。