返回介绍

第一部分 类型和语法

第二部分 异步和性能

5.6 try..finally

发布于 2023-05-24 16:38:21 字数 2611 浏览 0 评论 0 收藏 0

try..catch 对我们来说可能已经非常熟悉了。但你是否知道 try 可以和 catch 或者 finally 配对使用,并且必要时两者可同时出现?

finally 中的代码总是会在 try 之后执行,如果有 catch 的话则在 catch 之后执行。也可以将 finally 中的代码看作一个回调函数,即无论出现什么情况最后一定会被调用。

如果 try 中有 return 语句会出现什么情况呢? return 会返回一个值,那么调用该函数并得到返回值的代码是在 finally 之前还是之后执行呢?

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 {
        // 没有返回语句,所以没有覆盖
    }
}

function bar() {
    try {
        return 42;
    }
    finally {
        // 覆盖前面的 return 42
        return;
    }
}

function baz() {
    try {
        return 42;
    }
    finally {
        // 覆盖前面的 return 42
        return "Hello";
    }
}

foo();  // 42
bar();  // undefined
baz();  // Hello

通常来说,在函数中省略 return 的结果和 return; 及 return undefined; 是一样的,但是在 finally 中省略 return 则会返回前面的 return 设定的返回值。

事实上,还可以将 finally 和带标签的 break 混合使用(参见 5.1.3 节)。例如:

function foo() {
    bar: {
        try {
            return 42;
        }
        finally {
            // 跳出标签为bar的代码块
            break bar;
        }
    }

    console.log( "Crazy" );
    return "Hello";
}

console.log( foo() );
// Crazy
// Hello

但切勿这样操作。利用 finally 加带标签的 break 来跳过 return 只会让代码变得晦涩难懂,即使加上注释也是如此。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文