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 42return;}}console.log(bar())// undefined
function baz() {try {return 42;}finally {// 覆盖前面的 return 42return "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 只会让代码变得晦涩难懂。
