Web前端入门第 80 问:JavaScript 哪些地方需要 try...catch 异常捕获
<p>前端开发一直有种错觉,好像异常捕获都是后端的事,毕竟后端开发时如果不处理代码中的异常,有些资源得不到释放,极其容易导致内存泄漏。</p><p>前端由于 JS 的垃圾回收机制无需手动释放资源,反而不会怎么使用异常捕获代码中的错误。实际上任何编程语言,要写出一个健壮性的代码,都需要考虑异常处理。</p>
<p>本文分析下哪些地方可能需要使用异常捕获~~</p>
<h2 id="为什么需要-trycatch">为什么需要 try...catch</h2>
<p>看一个例子:</p>
<pre><code class="language-js">(() => {
function init() {
a()
b()
c()
}
function a() {
console.log('a 方法执行')
// 在执行 a 方法时候,代码有异常错误
JSON.parse('{a:1}')
}
function b() {
console.log('b 方法执行')
}
function c() {
console.log('c 方法执行')
}
init()
})()
</code></pre>
<p>以上代码在 <code>init</code> 函数中执行了多个方法,但 <code>a</code> 方法在执行后,由于 JSON.parse 格式化的数据有问题会代码报错,从而导致 <code>b</code> 和 <code>c</code> 方法无法执行。</p>
<p>而预期的执行逻辑是 a 方法执行完毕后,继续执行 b 和 c 方法。实际结果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250721093454130-568536314.png"></p>
<p>b 和 c 方法都未执行,而且用户也无法感知到任何异常,如果此方法放在了一个点击交互中,就会出现点击后没任何反馈,这肯定不是产品要的需求效果!!</p>
<p>稍稍优化:</p>
<pre><code class="language-js">(() => {
function init() {
a()
b()
c()
}
function a() {
console.log('a 方法执行')
try {
// 在执行 a 方法时候,代码有异常错误
JSON.parse('{a:1}')
} catch (error) {
// 给出用户提示
alert('程序异常,请联系管理员')
throw new Error('JSON.parse 错误')
// 或直接使用 throw error 抛出
}
}
function b() {
console.log('b 方法执行')
}
function c() {
console.log('c 方法执行')
}
init()
})()
</code></pre>
<p>优化后虽然代码还是无法执行 b 和 c 方法,但用户会感知到异常错误,对用户来说,至少有了一个交互反馈。</p>
<p>如果逻辑上 a 和 b、c 方法没有关联,那么 a 方法可以不用 <code>throw</code> 抛出错误,直接 <code>return</code> 即可,这样可以保证 b 和 c 方法顺利执行,比如:</p>
<pre><code class="language-js">function a() {
console.log('a 方法执行')
try {
// 在执行 a 方法时候,代码有异常错误
JSON.parse('{a:1}')
} catch (error) {
// 给出用户提示
alert('程序异常,请联系管理员')
return false
}
}
</code></pre>
<h2 id="使用-trycatch">使用 try...catch</h2>
<p>那么在 JS 中有哪些地方需要异常捕获呢??接下来看看各种场景~~</p>
<h3 id="同步代码">同步代码</h3>
<p>这类问题通常用于输入与预期不匹配,导致代码运行错误,如果不处理异常,可能会导致大面积的页面白屏或者没有任何反馈。</p>
<p>比如上面例子中的 <code>JSON.parse()</code> 格式化 JSON 数据时候,正确示例:</p>
<pre><code class="language-js">try {
JSON.parse(jsonString)
} catch (error) {
// 根据需要处理错误
}
</code></pre>
<h3 id="asyncawait-的异步代码">async/await 的异步代码</h3>
<p>如果 await 等待的 Promise 被 reject,那么代码也会抛出异常,如果不处理错误,也会出现没有交互反馈的样子。</p>
<p>网络请求是最常见的 await 错误,正确示例:</p>
<pre><code class="language-js">async function getData() {
try {
const res = await fetch('https://test.com/api')
const data = await res.json()
} catch (error) {
// 根据需要处理错误
}
}
</code></pre>
<h3 id="访问不确定的属性或方法">访问不确定的属性或方法</h3>
<p>这种情况通常是在访问后端返回数据的时候会出现异常,比如预期我们需要的是 string 类型,但后端返回的是 null,在针对 string 操作时候,就会导致代码异常。</p>
<p>正确示例:</p>
<pre><code class="language-js">function getUsers(res) {
try {
// res 是后端返回的数据
const users = res.list.split(',')
return users
} catch (error) {
// 根据需要处理错误
}
return []
}
getUsers({list: null})
</code></pre>
<p>或者使用 JS 提供的 <code>可选链操作符</code> 优化:</p>
<pre><code class="language-js">function getUsers(res) {
// res 是后端返回的数据
return res?.list?.split(',') ?? []
}
</code></pre>
<h3 id="执行三方代码">执行三方代码</h3>
<p>三方代码不确定性太大,在执行三方代码提供的 API 尤其需要注意异常处理。</p>
<p>正确示例:</p>
<pre><code class="language-html"><script src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.0/vconsole.min.js"></script>
<script>
try {
// 如果的 src 引入的 JS 出现网络问题,那么 `new VConsole()` 执行就会报错
new VConsole()
} catch (error) {
// 根据需要处理错误
}
// ... 后续的代码逻辑
</script>
</code></pre>
<h3 id="浏览器的-api-调用">浏览器的 API 调用</h3>
<p>浏览器提供的 API 也不全是能调用的,如果有版本兼容问题,在执行时候,代码也会出现异常错误。</p>
<p>以 <code>localStorage</code> 为例,如果用户禁用了本地存储,就有可能导致方法报错。记得 <strong>无痕模式</strong> 刚开始兴起那会儿,在无痕模式下访问本地存储就会报错!!</p>
<p>正确示例:</p>
<pre><code class="language-js">try {
localStorage.setItem('name', '前端路引');
} catch (error) {
// 根据需要处理错误
}
</code></pre>
<h3 id="nodejs-文件操作">nodejs 文件操作</h3>
<p>在服务端运行 nodejs 读取文件时候,尤其需要小心文件不存在时的异常问题。</p>
<p>正确示例:</p>
<pre><code class="language-js">try {
const content = fs.readFileSync('web3dev.txt', 'utf8');
} catch (error) {
// 根据需要处理错误
}
</code></pre>
<h3 id="用户输入校验">用户输入校验</h3>
<p>这种情况一般用于优化代码结构,直接在校验失败时候抛出异常,使用异常捕获统一处理提示信息。</p>
<p>正确示例:</p>
<pre><code class="language-js">function validateInput(input) {
if (!input) {
throw new Error('输入不能为空');
}
return true;
}
function isPhoneNumber(phone) {
if (/^1\d{9}$/.test(phone)) {
return true;
}
throw new Error('请输入正确的手机号码');
}
try {
const str = '123456789'
validateInput(str);
isPhoneNumber(str);
// ... 后续的代码逻辑
} catch (error) {
alert(error.message);
// 根据需要处理错误
}
</code></pre>
<h2 id="trycatch-其他用法">try...catch 其他用法</h2>
<p>try 的用法除了最常见的 <code>try...catch</code> 写法外,还有一些其他写法,比如:</p>
<h3 id="省略-error">省略 error</h3>
<pre><code class="language-js">try {
JSON.parse('{a:1}')
} catch {
// 根据需要处理错误
}
</code></pre>
<h3 id="使用-finally">使用 finally</h3>
<p>无论 try 或 catch 的代码中<strong>是否报错</strong>,也无论是否有 <strong>return</strong> 和 <strong>throw</strong> 关键字。<code>finally</code> 的代码块都会正常执行,常用于资源释放,比如关闭文件流、处理数据库连接、关闭页面 loading 等。</p>
<pre><code class="language-js">function init() {
try {
console.log(1)
JSON.parse('{a:1}')
} catch {
console.log(2)
// 根据需要处理错误
return false
} finally {
console.log(3)
}
}
init()
</code></pre>
<p>执行顺序:</p>
<pre><code class="language-bash">1
2
3
</code></pre>
<h3 id="省略-catch">省略 catch</h3>
<p>虽然 catch 可以省略,但缺少了 catch 捕获异常之后,代码不会往下执行,finally 的代码块不影响,比如:</p>
<pre><code class="language-js">try {
JSON.parse('{a:1}')
} finally {
// 根据需要处理错误
console.log(1)
}
console.log(2)
</code></pre>
<p>执行结果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250721093501750-1615316690.png"></p>
<p><strong>catch 和 finally 两个至少要有一个,否则代码报错。</strong></p>
<h2 id="异步任务异常错误">异步任务异常错误</h2>
<p><code>try...catch</code> 无法捕获异步任务中的异常错误,比如 Promise 中的异常错误,必须使用 Promise.catch 来捕获;setTimeout 的内部异常也无法使用 try 处理。</p>
<pre><code class="language-js">try {
new Promise((resolve, reject) => {
throw new Error("测试错误");
}).catch((err) => {
// 这里代码会执行
console.log('Promise 内部错误', err);
})
} catch {
console.log('不会执行')
}
try {
setTimeout(() => {
throw new Error("测试错误");
}, 100);
} catch {
console.log('不会执行')
}
</code></pre>
<h2 id="写在最后">写在最后</h2>
<p>任何事物都是犹过不及,合理使用 <code>try...catch</code> 可以有效提高代码健壮性,但过渡使用也容易造成代码冗余,所以编码也需要考虑分寸,拿捏得合适,则是优雅舞者。</p>
<p><strong>编码注意:不要静默吞掉错误,至少使用 console 记录错误信息(如 console.error),否则会影响程序问题排查。</strong></p>
</div>
<div id="MySignature" role="contentinfo">
<p> </p>
<p style="font-size: 18px;font-weight: bold;">文章首发于微信公众号【<span style="color:rgb(255, 71, 87)">前端路引</span>】,欢迎 <span style="color:#4ec259">微信扫一扫</span> 查看更多文章。</p>
<p>
<img style="max-width: 320px;" src="https://images.cnblogs.com/cnblogs_com/linx/2447020/o_250228035031_%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.png"/>
</p>
<p>本文来自博客园,作者:前端路引,转载请注明原文链接:https://www.cnblogs.com/linx/p/18995228</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/linx/p/18995228
頁:
[1]