6

JavaScript 系列四:流程控制

 3 years ago
source link: https://segmentfault.com/a/1190000039991642
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

JavaScript 系列四:流程控制

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

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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK