6
头图
"Code tailor",为前端开发者提供技术相关资讯以及系列基础文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。

前言

在开始学习之前,我们想要告诉您的是,本文章是对JavaScript语言知识中 "流程控制" 部分的总结,如果您已掌握下面知识事项,则可跳过此环节直接进入题目练习

  • if 语句
  • while、do-while 语句
  • for 语句、for-in 语句、for-of 语句
  • break 语句和 continue 语句
  • switch 语句
  • try/catch 语句

汇总总结

ECMA-262 描述了一些语句(也称为流控制语句),而 ECMAScript 中的大部分语法都体现在语句中。 语句通常使用一或多个关键字完成既定的任务。语句可以简单,也可以复杂。简单的如告诉函数退出,复杂的如列出一堆要重复执行的指令。

if 语句

if 语句是使用最频繁的语句之一,语法如下:

if (condition) {
  statement1
} else {
  statement2
}

这里的条件(condition)可以是任何表达式,并且求值结果不一定是布尔值。ECMAScript 会自动调用 Boolean() 函数将这个表达式的值转换为布尔值。如果条件求值为 true,则执行语句 statement1;如果条件求值为 false,则执行语句 statement2。这里的语句可能是一行代码,也可能是一个代码块(即包含在一对花括号中的多行代码)如下列代码:

if (xhs > 1) {
  console.log('xhs满足大于1!')
} else {
  console.log('xhs不满足大于1!')
}

上面代码当 xhs 满足大于 1 时,输出 xhs满足大于 1!,当 xhs 不满足大于 1 时,输出 xhs 不满足大于 1!

可以像这样连续使用多个 if 语句:

if (condition1) {
  statement1
} else if (condition2) {
  statement2
} else {
  statement3
}

下面是一个例子:

if (xhs > 25) {
  console.log('xhs满足大于25')
} else if (xhs < 0) {
  console.log('xhs满足小于0')
} else {
  console.log('xhs在0到25之间')
}

do-while 语句

do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行一次do-while 的语法如下:

do {
  statement
} while (expression)

下面是一个例子:

let xhs = 0
do {
  xhs += 2
} while (xhs < 10)

在上面的例子中,变量 xhs 先加了 2,然后用 2 去与10 做比较,如果小于 10 ,循环就会重复执行,直到 xhs >= 10

注意 后测试循环经常用于这种情形:循环体内代码在退出前至少要执行一次。

while 语句

while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。因此,while 循环体内的代码有可能不会执行。下面是 while 循环的语法:

while (expression) {
  statement
}

这是一个例子:

let i = 0
while (i < 10) {
  i += 2
}

在这个例子中,变量 xhs0 开始,每次循环递增 2。只要 xhs 小于 10,循环就会继续。

for 语句

for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码(initialization),以及循环执行后要执行的表达式(loop-expression),语法如下:

for (initialization; expression; loop - expression) {
  statement
}

下面是一个用例:

let xhsLength = 10
for (let xhs = 0; xhs < xhsLength; xhs++) {
  console.log(xhs)
}

以上代码在循环开始前定义了变量 xhs 的初始值为 0。然后求值条件表达式,如果求值结果为 true (xhs < xhsLength),则执行循环体。因此循环体也可能不会被执行。如果循环体被执行了,则循环后表达式也会执行,以便递增变量 xhsfor 循环跟下面的 while 循环是一样的:

let xhsLength = 10
let xhs = 0
while (xhs < xhsLength) {
  console.log(xhs)
  xhs++
}

无法通过 while 循环实现的逻辑,同样也无法使用 for 循环实现。因此 for 循环只是将循环相关的代码封装在了一起而已。

for 循环的初始化代码中,其实是可以不使用变量声明关键字的。不过,初始化定义的迭代器变 量在循环执行完成后几乎不可能再用到了。因此,最清晰的写法是使用 let 声明迭代器变量,这样就可以将这个变量的作用域限定在循环中。

初始化、条件表达式和循环后表达式都不是必需的。因此,下面这种写法可以创建一个无穷循环:

for (;;) {
  // 无穷循环
  doSomething()
}

如果只包含条件表达式,那么 for 循环实际上就变成了 while 循环:

let xhsLength = 10
let xhs = 0
for (; xhs < xhsLength; ) {
  console.log(xhs)
  xhs++
}

这种多功能性使得 for 语句在这门语言中使用非常广泛。

for-in 语句

for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,语法如下:

for (property in expression) {
  statement
}

下面是一个例子:

for (const xhsName in object) {
  console.log(xhsName)
}

这个例子使用 for-in 循环显示了对象 object 的所有属性。每次执行循环,都会给变量 xhsName 赋予一个 object 对象的属性作为值,直到 object 的所有属性都被枚举一遍。与 for 循环 一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const

ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序。换句话说,所有可枚举的属性都会返回一次,但返回的顺序可能会因浏览器而异。

如果 for-in 循环要迭代的变量是 null undefined,则不执行循环体。

for-of 语句

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素,语法如下:

也就是说只要原型对象实现了@@iterator方法,该对象即可被for-of
for (property of expression) {
  statement
}

下面是示例:

for (const xhsName of [2, 4, 6, 8]) {
  console.log(xhsName) //分别打印2,4,6,8
}

在这个例子中,我们使用 for-of 语句显示了一个包含 4 个元素的数组中的所有元素。循环会一直持续到将所有元素都迭代完。与for循环一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const

for-of 循环会按照可迭代对象的 next() 方法产生值的顺序迭代元素。关于可迭代对象,请详见 ES6 系列的 Iterator 篇。

如果尝试迭代的变量不支持迭代,则 for-of 语句会抛出错误。

break 和 continue 语句

breakcontinue 语句为执行循环代码提供了更严格的控制手段。其中,break 语句用于立即退出循环,强制执行循环后的下一条语句。而 continue 语句也用于立即退出循环,但会再次从循环顶部开始执行。下面看一个例子:

for (let xhs = 1; xhs < 10; xhs++) {
  if (xhs % 5 == 0) {
    break
  }
  xhsNumber++
}
console.log(xhsNumber) // 4

在上面的代码中,for 循环会将变量 xhs1 递增到 10。而在循环体内,有一个 if 语句用于检查 xhs 能否被整除(使用取模操作符)。如果是,则执行 break 语句,退出循环。变量 xhsNumber 的初始值为 0, 表示循环在退出前执行了多少次。当 break 语句执行后,下一行执行的代码是 console.log(num), 显示 4。之所以循环执行了 4 次,是因为当 xhs等于 5 时,break 语句会导致循环退出,该次循环不会执行递增 xhsNumber 的代码。如果将 break 换成 continue,则会出现不同的效果:

let xhsNumber = 0
for (let xhs = 1; xhs < 10; xhs++) {
  if (xhs % 5 == 0) {
    continue
  }
  xhsNumber++
}
console.log(xhsNumber) // 8

这一次,console.log 显示 8,即循环被完整执行了 8 次。当 xhs 等于 5 时,循环会在递增 xhsNumber 之前退出,但会执行下一次迭代,此时 xhs6。然后,循环会一直执行到自然结束,即 xhs 等于 10。最终 xhsNumber 的值是 8 而不是 9,是因为 continue 语句导致它少递增了一次。

breakcontinue 都可以与标签语句一起使用,返回代码中特定的位置。这通常是在嵌套循环中,如下面的例子所示:

let xhsNumber = 0
xhsRookies: for (let xhs = 0; xhs < 10; xhs++) {
  for (let xhsJJ = 0; xhsJJ < 10; xhsJJ++) {
    if (xhs == 5 && xhsJJ == 5) {
      break xhsRookies
    }
    xhsNumber++
  }
}
console.log(xhsNumber) // 55

在这个例子中,xhsRookies 标签标识的是第一个 for 语句。正常情况下,每个循环执行 10 次,意味着 xhsNumber++ 语句会执行 100 次,而循环结束时 console.log 的结果应该是 100。但是,break 语句带来了一个变数,即要退出到的标签。添加标签不仅让 break 退出内部循环,也会退出外部循环。当执行到 xhsxhsJJ 都等于 5 时,循环停止执行,此时 xhsNumber 的值是 55continue 语句也可以使用标签,如下面的例子所示:

let xhsNumber = 0
xhsRookies: for (let xhs = 0; xhs < 10; xhs++) {
  for (let xhsJJ = 0; xhsJJ < 10; xhsJJ++) {
    if (xhs == 5 && xhsJJ == 5) {
      continue xhsRookies
    }
    xhsNumber++
  }
}
console.log(xhsNumber) // 95

这一次,continue 语句会强制循环继续执行,但不是继续执行内部循环,而是继续执行外部循环。 当 xhsxhsJJ 都等于 5 时,会执行 continue,跳到外部循环继续执行,从而导致内部循环少执行 5 次,结 果 xhsNumber 等于 95

组合使用标签语句和 breakcontinue 能实现复杂的逻辑,但也容易出错。注意标签要使用描述性强的文本,而嵌套也不要太深。

switch 语句

switch 语句是与 if 语句紧密相关的一种流控制语句,如下所示:

switch (expression) {
  case value1:
    statement
    break
  case value2:
    statement
    break
  case value3:
    statement
    break
  case value4:
    statement
    break
  default:
    statement
}

这里的每个 case (条件/分支)相当于:“如果表达式等于后面的值,则执行下面的语句。”break 关键字会导致代码执行跳出 switch 语句。如果没有 break,则代码会继续匹配下一个条件。default 关键字用于在任何条件都没有满足时指定默认执行的语句(相当于 else 语句)。

有了 switch 语句,开发者就用不着写类似这样的代码了:

if (xhs == 25) {
  console.log('25')
} else if (xhs == 35) {
  console.log('35')
} else if (xhs == 45) {
  console.log('45')
} else {
  console.log('xhsRookies')
}

而是可以这样写:

switch (xhs) {
  case 25:
    console.log('25')
    break
  case 35:
    console.log('35')
    break
  case 45:
    console.log('45')
    break
  default:
    console.log('xhsRookies')
}

为避免不必要的条件判断,最好给每个条件后面都加上 break 语句。如果确实需要连续匹配几个条件,那么推荐写个注释表明是故意忽略了 break,如下所示:

switch (xhs) {
  case 25:
    console.log('25')
  /*跳过*/
  case 35:
    console.log('25 or 35')
    break
  case 45:
    console.log('45')
    break
  default:
    console.log('xhsRookies')
}

ECMAScriptswitch 赋予了一些独有的特性。首先,switch 语句可以用于所有数据类型(在很多语言中,它只能用于数值),因此可以使用字符串甚至对象。 其次,条件的值不需要是常量,也可以是变量或表达式。看下面的例子:

switch ('hello xhsRookies') {
  case 'hello' + ' xhsRookies':
    console.log('hello,xhs-rookies')
    break
  case 'goodbye':
    console.log('goodbye,xhs-rookies')
    break
  default:
    console.log('sorry,xhs-rookies')
}

这个例子在 switch 语句中使用了字符串。第一个条件实际上使用的是表达式,求值为两个字符串 拼接后的结果。因为拼接后的结果等于 switch 的参数,所以 console.log 会输出 hello,xhs-rookies。能够在条件判断中使用表达式,就可以在判断中加入更多逻辑:

let xhsNumber = 25
switch (true) {
  case xhsNumber < 0:
    console.log('xhsNumber less than 0.')
    break
  case xhsNumber >= 0 && xhsNumber <= 10:
    console.log('xhsNumber between 0 and 10.')
    break
  case xhsNumber > 10 && xhsNumber <= 20:
    console.log('xhsNumber between 10 and 20.')
    break
  default:
    console.log('xhsNumber more than 20.')
}

上面的代码首先在外部定义了变量 xhsNumber,而传给 switch 语句的参数之所以是 true,就是因为每个条件的表达式都会返回布尔值。条件的表达式分别被求值,直到有表达式返回 true。否则,就会一 直跳到 default 语句(这个例子正是如此)。

try/catch 语句

try/catch 语句,作为在 JavaScript 中处理异常的一种方式,基本的语法如下所示:

try {
  // 可能出错的代码
} catch (error) {
  // 出错时要做什么
}

任何可能出错的代码都应该放到 try 块中,而处理错误的代码则放在 catch 块中,如下所示:

try {
    dosome...
} catch (error) {
    console.log("An error happened!");
}

如果 try 块中有代码发生错误,代码会立即退出执行,并跳到 catch 块中。catch 块此时接收到一个对象,该对象包含发生错误的相关信息。

try/catch 语句中可选的 finally 子句始终运行。如果 try 块中的代码运行完,则接着执行 finally 块中的代码。如果出错并执行 catch 块中的代码,但 finally 块中的代码仍执行。trycatch 块无法阻止 finally 块执行,包括 return 语句

try {
  return 2
} catch (error) {
  return 1
} finally {
  return 0
}

在这个 try/catch 语句的各个部分都只放了一个 return 语句。看起来该函数应该返回 2, 因为它在 try 块中,不会导致错误。但是,finally 块的存在导致 try 块中的 return 语句被忽略。 因此,无论什么情况下调用该 try/catch 都会返回 0。如果去掉 finally 子句,该函数会返回 2。如果写出 finally 子句,catch 块就成了可选的。

自测题目

一、下列代码输出什么?

let xhsA = 1,
  xhsB = 0
while (xhsA <= 100) {
  xhsB = xhsB + xhsA
  xhsA++
}
console.log(xhsB)

let xhsC = 1,
  xhsD = 0
do {
  xhsD = xhsD + xhsC
  xhsA++
} while (xhsC <= 100)

console.log(xhsD)

let xhsE = 0
for (let xhsF = 1; xhsF <= 100; xhsF++) {
  xhsE += xhsF
}
console.log(xhsE)

二、下列代码输出什么?

let xhsType = 'teacher'
switch (xhsType) {
  case 'student':
    console.log('我是student!')
    break
  case 'teacher':
    console.log('我是teacher!')
  case 'parent':
    console.log('我是parent!或者 我是teacher')
    break
  default:
    console.log('我是person!')
}

三、下列代码输出什么?

try {
  let xhsNumber1 = 10
  console.log(xhsNumber1)
} catch (error) {
  let xhsNumber2 = 10 / 2
  console.log(xhsNumber2)
} finally {
  let xhsNumber3 = 10 / 5
  console.log(xhsNumber3)
}

题目解析

一、

Answer 5050 5050 5050

此题目考查的是 whiledo whilefor 循环语句,第一个 while 循环一开始定义 xhsA=1,满足小于等于 100,所以循环执行 xshB = xhsB + xhsA;xhsA++; 直到 xshA 不满足小于等于 100,退出循环,此循环等价于 1+2+3+4+······+99+100=5050;第二个 do while 循环,和 while 循环过程一样,唯一区别是,do while 循环不管什么条件,先执行一遍块内代码,然后判断 xhsC 是否满足小于等于 100,最后输出结果 5050;for 循环一开始定义 xhsF=1,满足小于等于 100,所以循环执行 xhsE+=xhsF; 直到 xhsF 不满足小于等于 100,退出循环,此循环也等价于 1+2+3+4+······+99+100=5050

二、

Answer 我是 teacher! 我是 parent!或者 我是 teacher

此题目考查的是 switch 循环语句,一开始定义 xhsType = 'teacher'; 传入 switch 语句中,匹配到 case 'teacher': 执行后面的语句,输出“我是 teacher!”,由于后面没有 break; 语句跳出循环,所以继续执行后面的语句输出“我是 parent!或者 我是 teacher”,遇到 break; 跳出循环

三、

Answer 10 2

此题目考察的是 try/catch 语句,在 try 块中定义了 xhsNumber1 的值为 10,然后打印输出,这个过程并没有错误抛出,所以 catch 块中代码不会被执行,但是 finally 中的代码,不管 try 块中有没有错误抛出,都会执行,所以输出 xhsNumber3 的值 2


小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。