finally
中的代码总会在 try
之后执行,如果有 catch
的话则在 catch
之后执行。可以将 finally
中的代码看作一个回调函数,即无论出现什么情况最后一定会被调用。
如果 try
中有 return
语句会出现什么情况呢?
function foo() {
try {
return 42;
}
finally {
console.log( "Hello" );
}
console.log( "never runs" );
}
console.log( foo() );
// Hello
// 42
这里return 42
先执行,并将foo()
函数的返回值设置为42
。然后try
执行完毕,接着执行 finally
。最后 foo()
函数执行完毕,console.log(..)
显示返回值。try
中的 throw
也是如此:
function foo() {
try {
throw 42;
}
finally {
console.log( "Hello" );
}
console.log( "never runs" );
}
console.log( foo() );
// Hello
// Uncaught Exception: 42
如果 finally
中抛出异常(无论是有意还是无意),函数就会在此处终止。如果此前 try
中已经有 return
设置了返回值,则该值会被丢弃:
function foo() {
try {
return 42;
}
finally {
throw "Oops!";
console.log( "never runs" );
}
}
console.log( foo() );
// Uncaught Exception: Oops!
continue
和 break
等控制语句也是如此:
for (var i=0; i<10; i++) {
try {
continue;
}
finally {
console.log( i );
}
}
// 0 1 2 3 4 5 6 7 8 9
continue
在每次循环之后,会在 i++
执行之前执行 console.log(i)
,所以结果是 0..9
而非1..10
。
ES6 中新加入了 **yield**
,可以将其视为 **return**
的中间版本。然而与 **return**
不同的是,**yield**
在 **generator**
(ES6 的另 一个新特性)重新开始时才结束,这意味着**try { .. yield .. }**
并未结束, 因此 **finally**
不会在 **yield**
之后立即执行。finally
中的 return
会覆盖 try
和 catch
中 return
的返回值:
function foo() {
try {
return 42;
}
finally {
// 没有返回语句,所以没有覆盖
}
}
console.log(foo())
// 42
function bar() {
try {
return 42;
}
finally {
// 覆盖前面的 return 42
return;
}
}
console.log(bar())
// undefined
function baz() {
try {
return 42;
}
finally {
// 覆盖前面的 return 42
return "Hello";
}
}
console.log(baz())
// Hello
通常来说,在函数中省略return
的结果和return;
及return undefined;
是一样的,但是在 finally
中省略 return
则会返回前面的 return
设定的返回值。
还可以将 finally
和带标签的 break
混合使用
function foo() {
bar: {
try {
return 42;
}
finally {
// 跳出标签为bar的代码块
break bar;
}
}
console.log( "Crazy" );
return "Hello";
}
console.log( foo() );
// Crazy
// Hello
切勿这样操作。利用 finally
加带标签的 break
来跳过 return
只会让代码变得晦涩难懂。