try-final中使用return的优先级问题

try...catch...finally 是JavaScript中常用的异常捕获语句。但是如果把 return 语句再牵扯进来的话,就开始有点意思了。 我们先看下如下这段代码:

1
2
3
4
5
6
7
8
9
10
var a = {b: 1}
function Test() {
try {
return a; // return执行完不会立即退出Test函数,而是会去执行finally
// 这里的代码不会执行
} finally {
// finally会在return执行之后再执行,执行完之后本Test函数才返回
a = {b: 2}
}
}

这段代码的运行效果是:

  1. 最开始打印a变量的时候, 是 {b: 1}
  2. 执行Test(),其返回值是 {b:1}
  3. 执行完了Test()函数之后,再去看下a变量,此时是 {b:2}

return与finally的执行先后顺序

return是会在finally之前执行的,但对于 try...finally 语句,try里面的return不会终止掉当前函数的执行,因为finally总是会执行,所以return会等到finally执行完毕才返回并结束本函数的执行。

JavaScript标准教程里面讲解finally语句时,也提到:

return语句的执行是排在finally代码之前,只是等finally代码执行完毕后才返回。

为什么Test返回的是{b:1} 而不是 {b:2}

上面原因已经说的很清楚了,return其实是优先于finally执行的,所以Test函数的返回值是 try 里面执行return的那一刻时候a的指针指向—也就是 {b:1} 这个对象。 而当finally执行时把a指针重新指向了{b:2}这个新对象,并不影响return返回给外面的第一个指针地址(返回的那个地址依然是指向 {b:1} 这个对象)

非指针类型,finally也不会影响返回值

看下如下代码:

1
2
3
4
5
6
7
8
9
var a = false
function Test() {
try {
return a; // return就是返回a的值,尽管函数要等到finally执行完毕才返回,但此时return已经记下来要返回的值是false
} finally {
// finally会修改a的值,但不影响上面return出去的false
a = true
}
}

之所以返回false,也是因为return是优先于finally执行的

指针类型,可以通过修改对象来修改返回值

既然原理搞清楚了,那么我们可以想到,如果是return一个对象,其实我们可以在finally里面修改对象来实现改变返回的内容的。demo如下:

1
2
3
4
5
6
7
8
var a = {b:1}
function Test() {
try {
return a;
} finally {
a.b = 2
}
}

尽管return时记录的是a指向的对象的指针,但是我们在finally里面修改的就是这个对象。因此return出去的值会打印为 {b:2}

什么时候只用 try...final... 而不用 catch ?

这在很多语言(包括JavaScript)中是可以的。甚至在某些场景下,这是合理的。 statckoverflow上面有这样一段回答:

It depends on whether you can deal with the exceptions that can be raised at this point or not.

If you can handle the exceptions locally you should, and it is better to handle the error as close to where it is raised as possible.

If you can’t handle them locally then just having a try / finally block is perfectly reasonable - assuming there’s some code you need to execute regardless of whether the method > succeeded or not. For example (from Neil’s comment), opening a stream and then passing that stream to an inner method to be loaded is an excellent example of when you’d need try { } > finally { }, using the finally clause to ensure that the stream is closed regardless of the success or failure of the read.

However, you will still need an exception handler somewhere in your code - unless you want your application to crash completely of course. It depends on the architecture of your > application exactly where that handler is.

大概意思是:

  1. 使用 try...catch...finally... 语法时,是否加 catch 取决于你是否有能力或者愿意去处理这个异常。 如果你能处理这个异常,那么最好自己catch住并就近处理它
  2. 如果你不能处理这个异常,那么你可以使用 try...finally...的语法—这样就不管try里面的代码能否执行成功还是失败,有异常也交给外层调用者处理,自己并不处理。
  3. 典型的使用场景案例: 打开一个文件流,并交给流处理相关程序来处理流。 这时就可以:

    1
    2
    3
    4
    5
    6
    try {
    var a = fs.openStream();
    process(a);
    } finally {
    fs.close(a)
    }

    如果process过程中出现异常,他可能会自己处理。或者抛出异常给外面。 但是我这段代码中并不会处理这个异常,但我总会去关闭这个流

  4. 尽管你可以在 try finally 里面不关心异常处理,但是你的代码中最好是要有异常处理的机制—比如调用这段代码的时候增加异常捕获的处理等。 除非你就希望在收到异常后让进程退出

refer

https://softwareengineering.stackexchange.com/questions/131397/why-use-try-finally-without-a-catch-clause/131400#comment328505_131400
https://blog.csdn.net/aitangyong/article/details/38146833