在第三部分中,我们来讲跳转语句。跳转语句顾名思义就是可以从程序的一个位置跳转到另外的一个位置。常见的跳转语句有break语句、continue语句、return语句、throw语句和try/catch/finally语句,在讲这些语句之前,我先来讲比较重要的标签。

一、标签语句

        语句是可以添加一个标签的,标签的写法是在语句前加一个标签名和冒号。如:

var  a = 1
	aa : if (true){
		bb : if (a == 1)
			break aa
		a = 2
	}
console.log(a)

        aa和bb都是条件语句的标签名。在这段代码中,break语句将会跳出aa条件语句,所以最后面返回的结果是1不是2。在程序中给语句定义一个标签,则可以在程序任何地方使用。通常使用标签的是条件语句和循环语句,只有break和continue才能够使用标签。标签的声明必须是一个合法的JavaScript标识符,标签的命名空间和变量或函数的命名空间不同,所以可以标签名与变量名相等。但是标签名不能和内部的标签名重名,如果两个语句块不相互嵌套的话可以用同名的。重要的一点是,一条语句可以有多个标签。



二、break语句

        break语句是跳出所在的代码块。如果单独的使用break语句的话只是跳出所在的层,如果有多个循环嵌套的时候,可以先使用标签,给每个循环语句设好标签名,然后在break语句的后面加上要跳转的位置即可。如:

aa : for (var a = 0; a < 10; a++) {
	bb : for (var b = 0; b < 10; b++) {
		cc : for (var c =0; c < 10; c++) {
			break aa
		}
	}
}
console.log(a)

        在这段代码中,当循环语句执行到最里面那一层的时候,就会触发break语句,break语句要跳出aa,所以最后返回的是0。break语句多数用于循环语句和switch语句中,当出现这种标签语句相互嵌套的时候,也可以使用,如第一个例子就是。如果第一个例子中,没有标签的话,则break在条件语句中是非法的,这个是需要注意的。还有就是不管有没有标签,break语句都不能跳出函数。



三、continue语句

        continue语句与break语句,只不过continue语句不是跳出循环,而是执行下一个循环。同样continue语句可以使用标签,只不过是在循环体内使用,如果不是在循环体内使用则报错。当程序执行到continue语句的时候,就会终止当前的循环逻辑,执行下一个逻辑,在不同循环中,表现的不同。在上一篇中讲了while和for循环,现在就继续补充。

        在while循环中,当执行了continue语句后,就会终止本次循环逻辑,跳转到while的圆括号内,对圆括号内的表达式进行检查,真的话则进行循环,假则跳出。

        在do/while循环中,当执行了continue语句后,程序直接跳到while语句里进行循环判断,之后再进行下一次循环。

        在for循环里,当执行了continue语句后,程序终止当前的循环逻辑,直接跳到for语句的圆括号内,计算器先进行操作,然后才进行条件判断。

        在for/in循环里,当执行了continue语句后,循环开始遍历下一个属性名,这个属性名赋给了指定的变量。


四、return语句

        return语句只能用在函数体中,主要作用是终止函数的执行并返回一个值给调用的函数。如果没有这个return语句,每次调用函数返回的是一个underfined。通常的写法是return后面跟一个表达式或值,如果return后面没有这些,返回的是一个underfined。


五、结合return写递归

        递归简单来说就是调用自身,很多情况下写递归都是运用到return这个语句的,究竟如何写一个递归呢,下面就从一个简单的阶乘说起:

function ha(a){
	if (a == 1) {
		return 1
	}else{
		var num = a
		return ha(a-1)*num
	}
}
alert(ha(3))

        首先,当调用ha()函数的时候传入一个3,3不等于1所以执行的是else语句、在else语句有return,return里面也有一个ha()函数的调用,这个时候a-1的值是2,所以继续调用ha()函数。由于这个时候2不等于1,所以同样也是进入了else语句里,在else语句中,return里的ha()函数里的参数是1,所以这里的ha()函数返回的是1,所以当ha()的参数为2的时候返回的是2,所以当ha()函数里的参数为3时,根据返回的2与num相乘,得到这个结果6。值得一提的是,如果没有这个if(a==1)这个语句,则这个递归无限执行下去,也就是说,递归除了调用自身外,还需要有一个终止条件来防止无限的调用。



六、throw语句

        在JavaScript中,如果你的代码有错误,当运行的时候就会在控制台那里报错,除了这个能够显式地抛出异常外,还可以通过throw语句来显式的抛出异常。如:

throw a

        在这条语句中,由于没有声明a变量,所以这条语句会抛出一条错误,提醒程序员声明这个变量。当程序出现异常的时候 ,则会停止执行后面的代码。通常throw语句是结合下面的try/catch/finally表达式语句来使用的,下面就来讲try/catch/finally语句。



七、try/catch/finally语句

        try/catch/finally语句是JavaScript的异常处理机制。其中,try从句定义了需要处理的异常所在的代码块,catch从句跟随在try从句之后,当try块内某处发生了异常时,调用catch内的代码逻辑。catch从句之后是finally从句,用来放置清理代码。不管try中有没有抛出异常,都会执行finally从句。并且,try/catch/finally从句内的语句都需要一个花括号,即便是只有一句语句或者没有语句。用法如下:

try{
	statement1
}catch(e){
	statement2
}finally{
	statement3
}

        在这段代码里,唯有try内的语句抛出异常才会执行catch语句,try语句中抛出异常通常是用throw或者用一个方法间接抛出异常。catch语句里的e指的是当捕获一个异常的时候,把和这个异常相关的值赋值给这个参数,这个值通常是个对象,如Error对象。在这里,catch语句具有它的块级作用域,这个是需要注意的。catch中可以通过throw重新抛出异常给另一个catch语句来接收,也就是说,在异常处理里,可以有多个catch语句。

        在异常处理中,即便是我们在try语句里使用诸如return、break或者continue语句使其发生跳转,如果异常处理中有finally语句,则无论如何都会先执行这个finally语句然后才跳转。如:

aa : if (true) {
	bb : if (true) {
		try{
			var a = 1
			break aa
		}finally{
			console.log(a)
		}
	}
}

        在这里得到的结果是1,也就证明上面的结论。如果try抛出异常但是没有catch语句的话,则会向上传播这个异常,直至有能够处理这个异常的catch语句。如果finally从句运行到了return语句,尽管已经抛出异常且这个抛出的异常还没有处理,这个方法还是会正常返回。如:

var foo = function(){
	try{
		throw a
	}finally{
		return 1
	}
}
foo()

        还是得到返回结果1。如果finally从句抛出一个异常,这个异常将代替正在抛出的异常。虽然我们可以通过try和finally来使while循环模拟的和for循环一样,但是,一旦statement语句内有break或者continue语句的时候,就有了微妙的差别,while语句总是会额外的计算了一次自增。如:


for(var a =1; a < 10; a++){
	console.log(a)
	if(a == 5)
		break
}
var b = 1
while(b < 10){
	try{
		console.log(b)
		if(b == 5)
			break
	}finally{
		b++
	}
}

        在这里,当程序执行到变量终于5的时候,都会有一个break语句。在for循环里会跳出这个循环并且a是不会再自增的,但是在while循环中,因为刚刚讲过,所以即便是try语句内有一个break语句,也是要先执行完finally语句才能跳转,所以这里多计算了一次b。这样也就印证了上一篇的结论,while循环无论如何也是不能完完全全的模拟出for循环的。