6.5 结构跳转
结构跳转语句主要包括标签、break、continue,它们常与条件语句、循环语句配合使用,来控制条件和循环流程,以提升代码运行效率。
6.5.1 标签语句
在JavaScript中,任何语句都可以添加一个标签,以便在复杂结构中设置程序跳转路径。定义标签语句的语法格式如下:
label : statements
label为任意合法的标识符,但不能使用保留字。由于标签名与变量名属于不同的语法体系,所以不用担心标签名与变量名重叠。然后使用冒号分隔标签名与标签语句。
【示例】在下面代码中,b就是标签名,而a就是对象的属性名,其中标签b就是对对象结构进行标记。
b:{ a:true }
由于标签名和属性名都属于标签范畴,不能重名,下面这种写法是错误的:
a:{ //标签语句的标记名 a:true //对象属性的标记名 }
对象属性的标识名可以访问属性:
var o={ a:true } alert(o.a); //通过对象成员标识符可以用对象成员
但是用户不能使用标签语句的标记名来引用被标记的语句,下面这种写法是错误的:
b:{ a:true } alert(b.a); //不能够使用标签语句的标记名来引用被标记的语句
标签语句常用在循环结构中,以从嵌套循环中跳出。由于标签语句必须与其他跳转语句配合使用,在下面小节中再介绍标签语句的应用。
6.5.2 break语句
break语句能够终止循环或多重分支结构的执行。主要用在循环结构和switch多重分支结构中,用在其他地方都是非法的。
break语句独立成句,用法如下:
break;
【示例1】break语句可以在循环结构中使用,用来退出循环结构。
for(var i = 0; ; i++) { if(i>=10)break; //通过break语句来监测for循环操作 alert(i); }
它等于:
for(var i = 0; i < 10; i ++ ) { alert(i); }
break关键字后面可以跟随一个标签名,用来指示程序终止执行之后要跳转的位置,并以该标签语句末尾的位置为起点继续执行。
break label;
【示例2】在本示例中,设计了一个3层嵌套的循环结构。分别为每层循环定义一个标签,然后在内部通过条件结构来判断循环变量值的变化,并利用带有标签名的break语句进行跳转。
a :for(var a = 0; a < 10; a ++ ){ b :for(var b = 0; b < 10; b ++ ){ c :for(var c = 0; c < 10; c ++ ){ if(c == 1){ alert("c=" + c); break c; } if(b == 1){ alert("b=" + b); break b; } if(a == 1){ alert("a=" + a); break c; } } } }
break语句和标签语句结合使用仅限于嵌套结构内部。
【示例3】在本示例中,设计3个并列的循环结构,企图在它们之间通过break语句和标签语句来实现相互跳转,这是不允许的。此时会提示编译错误,找不到指定的标签名。因为JavaScript在运行break语句时,仅限于当前结构或当前嵌套结构中寻找标签名。
a :for(var a = 0; a < 10; a ++ ){ if(a == 1){ alert("a=" + a); break b; } } b :for(var b = 0; b < 10; b ++ ){ if(b == 1){ alert("b=" + b); break c; } } c :for(var c = 0; c < 10; c ++ ){ if(c == 1){ alert("c=" + c); break a; } }
使用带有标签的break语句时应注意以下两点。
只有使用嵌套的循环或者嵌套的switch结构,且需要退出非当前层结构时,才可以使用带有标签的break语句。
break关键字与标签名之间不能够包含换行符,否则JavaScript会把它们看作是两个句子,并分别单独执行。
break语句的主要功能是提前结束循环或多重分支判断。这在循环条件复杂,且无法预控制的情况下,可以避免死循环或者不必要的空循环。
【示例4】在本示例中,设计在客户端查找document对象的bgColor属性。如果完全遍历document对象,会浪费很多时间。在for/in结构中添加一个if结构判断所枚举的属性名是否等于“bgColor”,如果相等,则使用break语句跳出循环结构。
for(i in document){ if(i.toString() == "bgColor"){ document.write("document." + i + "=" + document[i] + "<br />"); break; } }
在上面代码中,break语句并非跳出当前的if结构体,而是跳出当前最内层的循环结构。
【示例5】在下面嵌套结构中,break语句不是退出for/in循环体,而是退出switch结构体。
for(i in document){ switch(i.toString()){ case "bgColor": document.write("document." + i + "=" + document[i] + "<br />"); break; default: document.write("没有找到"); } }
【示例6】针对上面示例,如果需要退出外层的循环结构,就需要为for语句定义一个标签outloop,然后在break语句中指定该标签名,以便从最内层的多重分支结构中跳出最外层的for/in循环结构体。
outloop:for(i in document){ switch(i.toString()){ case "bgColor": document.write("document." + i + "=" + document[i] + "<br />"); break outloop; default: document.write("没有找到"); } }
6.5.3 continue语句
continue语句与break语句都独立成句,用于循环结构,break语句用于停止循环,而continue语句用于停止当前循环,继续执行下一次循环。
与break语句语法相同,continue语句可以跟随一个标签名,用来指定继续执行的循环结构的起始位置:
continue label;
【示例1】在本示例中,当循环变量等于4时,会停止循环体内最后一句的执行,返回for语句继续执行下一次迭代:
for(var i=0; i<10; i++) { alert(i); if(i==4)continue; //继续执行下一次迭代 alert(i); }
continue语句只能在循环结构(如while、do/while、for、for/in)内使用,在其他地方都会引发编译错误。当执行continue语句时,会停止当前迭代过程,开始执行下一次的迭代。但是对于不同的结构体,其继续执行的位置也略有不同。
对于for循环结构来说,将会返回执行for语句后第三个表达式,然后再执行第二个表达式,如果条件满足,则继续执行下一次迭代,如图6-4所示。
图6-4 continue语句在for结构中的执行路线图
对于for/in循环结构来说,将会以下一个赋给循环变量的属性名再次开始新的迭代。
对于while循环结构来说,将会返回再次检测while语句后的表达式,如果为true,则重新开始执行循环体内所有语句,如图6-5所示。
图6-5 continue语句在while结构中的执行路线图
【示例2】下面这个循环结构被执行时,将成为死循环。
var i=0; while(i<10){ alert(i); if (i==4) continue; i++ }
对于do/while循环结构来说,会跳转到底部的while语句先检测条件表达式,如果条件为true,则将从do语句后开始下一次的迭代,如图6-6所示。
图6-6 continue语句在do/while结构中的执行路线图
do/while结构与while结构比较相似,其中continue语句下面的语句将被忽略掉。但是在JavaScript 1.2版本中存在一个Bug,它将不检测底部的while语句后的循环条件,而是直接跳转到顶部do语句后面开始下一次迭代。所以,在使用do/while结构时应该注意这个安全风险。
【示例3】在本示例中,利用continue语句辅助过滤掉数组a中的字符串元素:
var a=[1, "d",2, "a", "b", "c",3,4] //定义并初始化数组a var b=[], j=0; //定义数组b和变量j for(var i in a){ //遍历数组a if(typeof a[i] == "string") //如果元素数据类型为字符串,则返回继续下一次迭代 continue; b[j++]=a[i]; //把非字符串类型的元素值赋值给数组b } alert(b); //返回1,2,3,4