前端开发一直有种错觉,好像异常捕获都是后端的事,毕竟后端开发时如果不处理代码中的异常,有些资源得不到释放,极其容易导致内存泄漏。

前端由于 JS 的垃圾回收机制无需手动释放资源,反而不会怎么使用异常捕获代码中的错误。实际上任何编程语言,要写出一个健壮性的代码,都需要考虑异常处理。

本文分析下哪些地方可能需要使用异常捕获~~

为什么需要 try...catch

看一个例子:

(() => {
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()
})()

以上代码在 init 函数中执行了多个方法,但 a 方法在执行后,由于 JSON.parse 格式化的数据有问题会代码报错,从而导致 bc 方法无法执行。

而预期的执行逻辑是 a 方法执行完毕后,继续执行 b 和 c 方法。实际结果:

b 和 c 方法都未执行,而且用户也无法感知到任何异常,如果此方法放在了一个点击交互中,就会出现点击后没任何反馈,这肯定不是产品要的需求效果!!

稍稍优化:

(() => {
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()
})()

优化后虽然代码还是无法执行 b 和 c 方法,但用户会感知到异常错误,对用户来说,至少有了一个交互反馈。

如果逻辑上 a 和 b、c 方法没有关联,那么 a 方法可以不用 throw 抛出错误,直接 return 即可,这样可以保证 b 和 c 方法顺利执行,比如:

function a() {
console.log('a 方法执行')
try {
// 在执行 a 方法时候,代码有异常错误
JSON.parse('{a:1}')
} catch (error) {
// 给出用户提示
alert('程序异常,请联系管理员')
return false
}
}

使用 try...catch

那么在 JS 中有哪些地方需要异常捕获呢??接下来看看各种场景~~

同步代码

这类问题通常用于输入与预期不匹配,导致代码运行错误,如果不处理异常,可能会导致大面积的页面白屏或者没有任何反馈。

比如上面例子中的 JSON.parse() 格式化 JSON 数据时候,正确示例:

try {
JSON.parse(jsonString)
} catch (error) {
// 根据需要处理错误
}

async/await 的异步代码

如果 await 等待的 Promise 被 reject,那么代码也会抛出异常,如果不处理错误,也会出现没有交互反馈的样子。

网络请求是最常见的 await 错误,正确示例:

async function getData() {
try {
const res = await fetch('https://test.com/api')
const data = await res.json()
} catch (error) {
// 根据需要处理错误
}
}

访问不确定的属性或方法

这种情况通常是在访问后端返回数据的时候会出现异常,比如预期我们需要的是 string 类型,但后端返回的是 null,在针对 string 操作时候,就会导致代码异常。

正确示例:

function getUsers(res) {
try {
// res 是后端返回的数据
const users = res.list.split(',')
return users
} catch (error) {
// 根据需要处理错误
}
return []
}
getUsers({list: null})

或者使用 JS 提供的 可选链操作符 优化:

function getUsers(res) {
// res 是后端返回的数据
return res?.list?.split(',') ?? []
}

执行三方代码

三方代码不确定性太大,在执行三方代码提供的 API 尤其需要注意异常处理。

正确示例:

<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>

浏览器的 API 调用

浏览器提供的 API 也不全是能调用的,如果有版本兼容问题,在执行时候,代码也会出现异常错误。

localStorage 为例,如果用户禁用了本地存储,就有可能导致方法报错。记得 无痕模式 刚开始兴起那会儿,在无痕模式下访问本地存储就会报错!!

正确示例:

try {
localStorage.setItem('name', '前端路引');
} catch (error) {
// 根据需要处理错误
}

nodejs 文件操作

在服务端运行 nodejs 读取文件时候,尤其需要小心文件不存在时的异常问题。

正确示例:

try {
const content = fs.readFileSync('web3dev.txt', 'utf8');
} catch (error) {
// 根据需要处理错误
}

用户输入校验

这种情况一般用于优化代码结构,直接在校验失败时候抛出异常,使用异常捕获统一处理提示信息。

正确示例:

function validateInput(input) {
if (!input) {
throw new Error('输入不能为空');
}
return true;
}
function isPhoneNumber(phone) {
if (/^1[3456789]\d{9}$/.test(phone)) {
return true;
}
throw new Error('请输入正确的手机号码');
} try {
const str = '123456789'
validateInput(str);
isPhoneNumber(str);
// ... 后续的代码逻辑
} catch (error) {
alert(error.message);
// 根据需要处理错误
}

try...catch 其他用法

try 的用法除了最常见的 try...catch 写法外,还有一些其他写法,比如:

省略 error

try {
JSON.parse('{a:1}')
} catch {
// 根据需要处理错误
}

使用 finally

无论 try 或 catch 的代码中是否报错,也无论是否有 returnthrow 关键字。finally 的代码块都会正常执行,常用于资源释放,比如关闭文件流、处理数据库连接、关闭页面 loading 等。

function init() {
try {
console.log(1)
JSON.parse('{a:1}')
} catch {
console.log(2)
// 根据需要处理错误
return false
} finally {
console.log(3)
}
}
init()

执行顺序:

1
2
3

省略 catch

虽然 catch 可以省略,但缺少了 catch 捕获异常之后,代码不会往下执行,finally 的代码块不影响,比如:

try {
JSON.parse('{a:1}')
} finally {
// 根据需要处理错误
console.log(1)
}
console.log(2)

执行结果:

catch 和 finally 两个至少要有一个,否则代码报错。

异步任务异常错误

try...catch 无法捕获异步任务中的异常错误,比如 Promise 中的异常错误,必须使用 Promise.catch 来捕获;setTimeout 的内部异常也无法使用 try 处理。

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('不会执行')
}

写在最后

任何事物都是犹过不及,合理使用 try...catch 可以有效提高代码健壮性,但过渡使用也容易造成代码冗余,所以编码也需要考虑分寸,拿捏得合适,则是优雅舞者。

编码注意:不要静默吞掉错误,至少使用 console 记录错误信息(如 console.error),否则会影响程序问题排查。

Web前端入门第 80 问:JavaScript 哪些地方需要 try...catch 异常捕获的更多相关文章

  1. web前端入坑第五篇:秒懂Vuejs、Angular、React原理和前端发展历史

    秒懂Vuejs.Angular.React原理和前端发展历史 2017-04-07 小北哥哥 前端你别闹 今天来说说 "前端发展历史和框架" 「前端程序发展的历史」 「 不学自知, ...

  2. web前端入坑第二篇:web前端到底怎么学?干货资料! 【转】

    http://blog.csdn.net/xllily_11/article/details/52145172 版权声明:本文为博主[小北]原创文章,如要转载请评论回复.个人前端公众号:前端你别闹,J ...

  3. web前端(13)—— 了解JavaScript,JavaScript的引入方式

    从本篇博文开始,将进入web前端方便最关键最重要的部分——javascript,学到后面你就知道它真的太重要了 什么是JavaScript JavaScript一种直译式的脚本语言,是一种动态类型.弱 ...

  4. WEB前端工程师整理的原生JavaScript经典百例

    一.原生JavaScript实现字符串长度截取 二.原生JavaScript获取域名主机 三.原生JavaScript转义html标签 四.原生JavaScript时间日期格式替换 Date.prot ...

  5. Web前端基础怎么学? JavaScript、html、css知识架构图

    以前开发者只要掌握 HTML.CSS.JavaScript 三驾马车就能胜任一份前端的工作了.而现在除了普通的编码以外,还要考虑如何性能优化,如何跨端.跨平台实现功能,尤其是 AI.5G 技术的来临, ...

  6. web前端学习之HTML CSS/javascript之一

    前端编码之路之坎坷,web前端应该一直是个战场吧,各种浏览器的不兼容,各种小细节的修改,要往一个好的产品经理方向走,实在是难,昨天听了一位十年经验的产品经理讲座,最重要的恐怕就是协调资源的能力,而协调 ...

  7. Android零基础入门第80节:Intent 属性详解(下)

    上一期学习了Intent的前三个属性,本期接着学习其余四个属性,以及Android系统常用内置组件的启动. 四.Data和Type属性 Data属性通常用于向Action属性提供操作的数据.Data属 ...

  8. web前端学习(四)JavaScript学习笔记部分(3)-- JavaScript函数+异常处理+事件处理

    1.Javascript函数-了解函数的用途 1.1.函数: 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块 2.Javascript函数-定义函数 2.1.function必须小写 3. ...

  9. 文成小盆友python-num14 - web 前端基础 html ,css, JavaScript

    本部分主要内容 html - 基础 css - 基础 一.html 标签 html 文档标签树如下: head 部分 Meta(metadata information) 提供有关页面的元信息,例:页 ...

  10. Web前端开发规范【HTML/JavaScript/CSS】

    前言 这是一份旨在增强团队的开发协作,提高代码质量和打造开发基石的编码风格规范,其中包含了 HTML, JavaScript 和 CSS/SCSS 这几个部分.我们知道,当一个团队开始指定并实行编码规 ...

随机推荐

  1. 使用PowerShell开发脚本程序进行批量SVN提交

    使用PowerShell开发脚本程序进行批量SVN提交 随着软件开发的不断进步,版本控制系统如Subversion (SVN) 成为了团队协作和代码管理的重要工具.当需要一次性提交大量文件时,手动操作 ...

  2. servlet @WebServlet注解

    web开发中可以通过web.xml写servlet标签表明一个类是Servlet,servlet3.0后可以使用@WebServlet表示一个类为Servlet. @WebServlet 参数 说明 ...

  3. 【记录】C语言|获取其他文件的绝对路径转为相对路径的两种代码

    GetModuleFileName获取当前程序的绝对路径.然后对比再strcat之类的. 只能获得同一盘符下的相对路径: #include <string.h> /*pa是绝对路径,rel ...

  4. 【HUST】网络攻防实践|5_二进制文件补丁技术|实验三 热补丁

    文章目录 实验要求 实验过程 1. 64位Ubuntu下先安装32位库 2. 利用Preload Hook实现热补丁修补 3. 利用系统调用`ptrace`对运行状态的程序进行hook 3.1 编写补 ...

  5. vue3 基础-传送门 teleport

    之前介绍了一波混入 mixin 和 自定义指令 directive 其基本作用就是为了在 vue 中实现代码的复用. 而本篇介绍的是 vue3 的一个新特性叫做传送门. 一听这个名字是不是就感觉特别科 ...

  6. svchost.exe占用端口

    事情是这样的,我写的某个程序基于tcp协议与其他程序进行通信,但每隔一段时间,该程序就不能与其他程序正常交流,用 telnet 127.0.0.1 1000 显示连接失败,但是程序的确开启,应该在监听 ...

  7. 洛谷题解:B4152 [厦门小学生 C++ 2022] 方阵排序

    题目传送门. 输入 #1 3 5 50 65 70 80 90 90 95 100 输出 #1 9 8 7 6 5 3 3 2 1 思路 把问题转化为求一个数在一堆数里的排名. 这时候直接用二分查找, ...

  8. 【mklink】创建符号链接

    场景 我通常用这个指令将C盘的文件转移到D盘中,或者是把一定要安装在C盘的软件所占用的空间转移到D盘.但是常常不记得参数和顺序. 指令通用格式 C盘的文件转移到D盘中 假设要把C:\abc\这个文件夹 ...

  9. 阿里也出手了!灵码AI IDE问世

    大家好,我是晓凡. 写在前面 各位程序员小伙伴们,是不是还在为写代码头秃?别担心,阿里云带着它的通义灵码 AI IDE 来拯救你啦! 相信不少小伙伴已经在VSCode.JetBrains IDE等主流 ...

  10. [UOJ618]【JOISC2021】聚会 2

    #618. [JOISC2021]聚会 2 就是相当于选中的点在整棵树上的重心 首先,当\(i\)为奇数时,答案为\(1\) 当\(i\)为偶数时,可以将选中的点分为两个子树,分别记其根节点为\(x\ ...