跳转至

语句

if 语句

if 语句的格式如下:

if (statement_A) {
    statements;
} else if (statement_B) {
    statements;
} else {
    statements;
}

注意,JavaScript 语言规定,else 总是和它上面最近的一个 if 配对,这与 C 语言一致。因此,类似于if (A) if (B) C; else D; 形式的语句应该理解为:

if (A)
    if (B)
        C;
    else
        D;

switch 语句

switch 语句的格式如下:

switch (控制表达式) {
    case 表达式:
        语句列表
    case 表达式:
        语句列表
    ...
    default
        常量表达式: 语句列表
}

switch 语句使用 === 判断控制表达式的值是否和某个常量表达式相等。分支的表达式可以相同,程序不会因此马上抛出错误。如果控制表达式不等于任何一个常量表达式,则从default分支开始执行,通常把default分支写在最后,但不是必需的。

使用 switch 语句要注意以下几点:

  1. 进入case后如果没有遇到break语句就会一直往下执行,后面其他casedefault分支的语句也会被执行到,直到遇到break,或者执行到整个switch语句块的末尾。通常每个case后面都要加上break语句,但有时会故意不加break,例如在函数体中使用 switch 语句,用 return 代替 break
  2. 与 C 语言不同,JavaScript case 后面的表达式可以不是常量表达式,表达式的值也可以不是整型。ECMAScript 标准允许case子句后面可以有任意表达式。
  3. default: 标签通常出现在 switch 语句的末尾,这只是一个逻辑上通用的地方,实际上它可以出现在语句主体内的任何位置。

switch 语句不是必不可少的,显然可以用一组 if...else if...else if...else... 代替,但是一方面用 switch 语句会使代码更清晰。

while 语句

initialize;
while(test){
    statements;
    increment;
}

例如:

let count = 0;
while(count < 10) {
    console.log(count);
    count++;
}

变量count循环变量(Loop Variable),每次循环都要改变它的值,在控制表达式中要测试它的值,这两点合起来起到控制循环次数的作用,在这个例子 中的值是递减的,也有些循环采用递增的循环变量。

do/while 语句

initialize;
do{
    statements;
    increment;
}while(test)

for 语句

for (initialize; test; increment) {
    statement
}

for 循环的语法格式等价于 while 循环的语法格式,从这种等价的形式来看,initialize表达式和increment表达式都可以为空,但test表达式是必不可少的。例如

for (;1;){...}
// 等价于
while(1){...}

因此,ECMAScript 标准规定,如果test表达式为空,则认为test表达式的值为真,因此死循环也可以写成

for (;;){...}

for/of 语句

ES6 定义了一个新的循环语句:for/of。这种新的循环使用 for 关键字,但是与常规的 for 循环完全不同。for/of 循环适用于可迭代对象。

let data = [1, 2, 3, 4, 5, 6, 7, 8, 9], sum = 0;
for(let element of data) {
    sum += element;
    data.push(sum)
}
sum       // => 45

goto 语句和异常处理

goto 语句可以实现无条件跳转。我们知道break只能跳出最内层的循环,如果在一个嵌套循环中遇到某个错误条件需要立即跳出最外层循环做出错处理,就可以用goto语句,例如:

for (...){
    for (...){
        ...
        if (出现错误条件)
            goto error;
    }
}
error:
    出错处理;

这里的error:叫作标号(Label),任何语句前面都可以加若干个标号,每个标号的命名也要遵循标识符的命名规则。

goto 语句过于强大了,从程序中的任何地方都可以无条件跳转到任何其他地方,只要在那个地方定义一个标号就行,唯一的限制是goto 只能跳转到同一个函数 中的某个标号处,而不能跳到别的函数中。滥用goto 语句会使程序的控制流程非常复杂,可读性很差。著名的计算机科学家 Edsger W.Dijkstra 最早指出编程语 言中goto语句的危害,提倡取消goto 语句。goto 语句不是必须存在的,显然可以用别的办法替代,比如上面的代码段可以改写为以下形式:

int cond = 0; /* bool variable indicating error condition */
for (...){
    for (...){
        ...
        if (出现错误条件){
            cond = 1;
            break;
        }
    }
    if (cond)
        break;
}
if (cond)
    错误处理;

通常goto 语句只用于这种场合,一个函数中任何地方出现了错误条件都可以立即跳转到函数末尾进行出错处理(例如释放先前分配的资源、恢复先前改动过的 全局变量等),处理完之后函数返回。比较用goto 和不用goto 的两种写法,用goto 语句还是方便很多。但是除此之外,在任何其他场合都不要轻易考虑使用goto 语句。

有些编程语言 (如 C++) 中有异常(Exception)处理的语法,可以代替gotosetjmp/longjmp的这种用法。casedefault后面也要跟冒号,事实上它们是两种特殊的标号

C标准库函数setjmplongjmp配合起来可以实现函数间的跳转,但只能从被调用的函数跳回到它的直接或间接调用者(同时从栈空间弹出一个或多个栈顿),而不能从一个函数跳转到另一个和它毫不相干的函数中。setjmp/longjmp函数主要也是用于出错处理,比如函数A调用函数B,函数B调用函数C,如果在C中出现某个错误条件,使得函数BC继续执行下去都没有意义了,可以利用setjmp/longjmp机制快速返回到函数A进行出错处理,本书不详细介绍这种机制,有兴趣的读者可参考《Advanced Programming in the UNIX Environment》的第7.10节和第10.15节。