Web前端入門第 80 問:JavaScript 哪些地方需要 try...catch 異常捕獲
前端開發一直有種錯覺,好像異常捕獲都是后端的事,畢竟后端開發時如果不處理代碼中的異常,有些資源得不到釋放,極其容易導致內存泄漏。
前端由于 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 格式化的數據有問題會代碼報錯,從而導致 b 和 c 方法無法執行。
而預期的執行邏輯是 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 的代碼中是否報錯,也無論是否有 return 和 throw 關鍵字。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),否則會影響程序問題排查。

浙公網安備 33010602011771號