ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JavaScript 中 `finally` 为何不可替代:对比 `try...catch` 之后的普通清理代码

JavaScript 中 `finally` 为何不可替代:对比 `try...catch` 之后的普通清理代码 文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载导读在 JavaScript 的异常处理中try...catch之后既可以追加一个finally子句也可以直接写一段收尾代码二者在无跳转时行为完全一致。本文以 Modern JavaScript Tutorial本仓库 1-js/10-error-handling/1-try-catch 章节中的经典辨析题为线索深入对比这两种写法揭示finally在函数内部发生return或throw等跳转退出时的关键语义差异并通过可运行的示例代码帮助读者掌握何时必须使用finally来保证清理逻辑的确定性执行。题目回顾两段看似等价的清理代码原题1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md给出了两个代码片段要求判断它们是否完全等价片段一使用finally执行清理try { // work work } catch (err) { // handle errors } finally { // cleanup the working space }片段二把清理代码直接放在try...catch之后try { // work work } catch (err) { // handle errors } // cleanup the working space需求很明确无论是否发生错误清理动作都必须执行。仅从这个需求看两种写法似乎都能满足——如果try块正常结束两段清理代码都会运行如果抛出了错误并被catch捕获两段清理代码也都会在catch结束后运行。那么这两段代码真的等价吗答案是否定的而差异恰恰隐藏在函数内部 跳转退出这一组合场景中。官方解答见 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md。核心差异finally能覆盖跳转退出官方解答给出的结论是当代码位于函数内部并且存在跳出try...catch的行为时两种写法产生明显差异。这里的跳出包含两种常见情形try块或catch块中的return语句catch块中无法处理错误时执行的throw重新抛出。finally子句对try...catch的任何退出路径都生效——包括return和throw它在try...catch结束之后、控制权交还给调用方之前运行。而放在函数末尾的普通代码在这些跳转场景下根本不会执行。场景一return跳出try...catch以下代码来自官方解答try块中执行了return result但finally中的清理仍然会运行function f() { try { alert(start); return result; } catch (err) { // ... } finally { alert(cleanup!); } } f(); // 输出: start, 然后 cleanup!执行流程是try块执行alert(start)遇到return时不会立即把控制权交还给调用方而是先执行finally中的alert(cleanup!)之后函数才真正返回。如果你把cleanup挪到函数末尾return会直接跳过它清理逻辑永远不会执行。场景二throw重新抛出错误再看catch块中重新抛出错误的场景catch捕获了错误但判断自己无法处理于是执行throw err。此时控制流同样会跳出try...catch而finally依然会在错误继续向外传播之前执行function f() { try { alert(start); throw new Error(an error); } catch (err) { // ... if (cant handle the error) { throw err; } } finally { alert(cleanup!) } } f(); // 输出: start, 然后 cleanup!最后错误向外抛出官方解答明确指出正是finally在这里保证了清理的执行。如果只是把代码放在函数f的末尾在这些场景下它根本不会运行。finally的完整语义任何退出路径都执行语法与执行路径finally是try...catch的可选第三个子句完整语法如下见 article.md 的 try...catch...finally 一节try { // 尝试执行代码 } catch (err) { // 处理错误 } finally { // 无论何种结果总是执行 }只要finally存在它就在所有情况下运行try块无错误时在try之后执行try块有错误时在catch之后执行。下图展示了try...catch的两条基本执行路径图片来源try-catch-flow.svg交互式验证主文档中提供了一个可直接在浏览器运行的验证示例弹窗询问是否制造一个错误回答 Yes 时执行顺序为try - catch - finally回答 No 时为try - finallytry { alert(try); if (confirm(Make an error?)) BAD_CODE(); } catch (err) { alert(catch); } finally { alert(finally); }无论哪条路径finally都必然出现——这正是它作为无论如何都要收尾这一承诺的实现基础。实战用例用finally保证耗时测量不遗漏finally最常见的应用场景是开始一项需要收尾的工作如计时、关闭资源无论成功或失败都必须完成收尾。主文档给出了测量fib(n)执行时间的完整示例let num prompt(Enter a positive integer number?, 35); let diff, result; function fib(n) { if (n 0 || Math.trunc(n) ! n) { throw new Error(Must not be negative, and also an integer.); } return n 1 ? n : fib(n - 1) fib(n - 2); } let start Date.now(); try { result fib(num); } catch (err) { result 0; } finally { diff Date.now() - start; } alert(result || error occurred); alert(execution took ${diff}ms);这里fib对负数或非整数输入会抛出错误。通过把diff Date.now() - start放进finally无论fib成功返回还是抛出异常耗时都能被正确记录输入35正常执行finally在try之后运行输出真实耗时输入-1立即抛错catch将result置为 0finally仍会运行耗时记录为约0ms。主文档对此的总结是函数无论是通过return还是throw结束finally都会执行。变量作用域的注意点上面的例子中result和diff都声明在try...catch之前这是有意为之如果在try块内用let声明变量只在其内部可见finally和catch都将无法访问。涉及多个块共享数据时务必把声明提升到try...catch...finally之外。finally与return、throw的细节语义finally在return之前的执行时机主文档用func()演示了return与finally的先后关系finally在控制权返回调用方之前执行因此调用方看到的效果是先弹出finally的 alert再得到返回值function func() { try { return 1; } catch (err) { /* ... */ } finally { alert(finally); } } alert(func()); // 先执行 finally 中的 alert再执行本行不带catch的try...finally当不想在这里处理错误让错误继续向上传播但必须保证已启动的工作被收尾时可以省略catch只写try...finallyfunction func() { // 开始一件需要收尾的事情如计时 try { // ... } finally { // 即使一切崩溃也要完成收尾 } }在这种写法中try块内的错误因为没有catch而必然向外抛出但finally会在执行流离开函数前先行运行。这与前文throw场景的结论相互印证finally覆盖的是任何退出包括异常路径。结论与判断准则回到题目本身两段代码并不等价。判断依据如下场景finally清理紧随其后的普通清理代码try正常结束✅ 执行✅ 执行catch正常结束✅ 执行✅ 执行try/catch中return跳出✅ 执行❌ 被跳过catch中throw重新抛出✅ 执行❌ 被跳过外层嵌套try...catch捕获场景✅ 执行❌ 被跳过实用准则当清理代码与try...catch处于同一函数且函数内存在return或重新抛错throw err的可能性时请使用finally而不是事后代码当清理代码位于顶层不在函数内且不存在跳转路径时两种写法行为一致但使用finally依然能提高代码意图的清晰度让无论如何都要执行的语义一目了然。延伸阅读本章主文档1-js/10-error-handling/1-try-catch/article.md含try...catch基础语法、错误对象、throw、重新抛出与全局捕获window.onerror的完整讲解本题目原文task.md官方解答solution.md流程示意图try-catch-flow.svg下一章自定义错误类型2-custom-errors赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐IsaacLab 执行器配置避坑DCMotor 的 4 个关键参数一次讲透IsaacLab 执行器配置避坑DCMotor 的 4 个关键参数一次讲透 做 IsaacLab 电机执行器配置时执行器模型选错机器人会表现得不像自己人工智能强化学习机器人具身智能深度学习AltStore终极指南无需越狱三步安装iOS第三方应用的完整教程AltStore终极指南无需越狱三步安装iOS第三方应用的完整教程 还在为App Store的限制而烦恼吗想要在iPhone上自由安装各种有趣的应用但又移动开发php-src异常处理try/catch/finally在Zend引擎中的实现php src异常处理try/catch/finally在Zend引擎中的实现 引言异常处理的重要性 在PHP开发中异常处理Exception Hand编程语言语言运行时解释器上一篇深入理解 ESLint 扩展体系插件、共享配置、自定义格式化器与解析器完全指南下一篇Karakeep 在 Unraid 上的完整部署指南Docker Compose Manager 与 Community Apps 双路径实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表