JS 中 try/catch/finally 關鍵詞的使用解析
在 JavaScript 中,try/catch/finally是處理代碼異常的核心關鍵詞組合,用於捕獲程序運行時的錯誤、避免程序崩潰,並執行必要的收尾邏輯。try包裹可能拋出錯誤的代碼塊,catch捕獲並處理錯誤,finally無論是否發生錯誤都會執行(用於釋放資源、清理環境等)。這一組合是保障代碼健壯性的關鍵,尤其在異步操作、數據解析、接口請求等易出錯場景中不可或缺。
一、try/catch/finally 的基礎用法
1. 核心語法結構
try {
// 可能拋出錯誤的代碼(try塊)
riskyCode();
} catch (error) {
// 捕獲錯誤並處理(catch塊)
handleError(error);
} finally {
// 無論是否出錯,都會執行的代碼(finally塊,可選)
cleanUp();
}
-
try塊:必須存在,包裹易出錯邏輯,執行過程中若拋出錯誤,立即跳轉到catch塊;
-
catch塊:可選,接收錯誤對象(包含錯誤信息、類型、堆棧等),僅在try塊拋出錯誤時執行;
-
finally塊:可選,無論try塊是否出錯、catch塊是否執行,最終都會執行(即使try/catch中有return)。
2. 基礎示例:捕獲簡單錯誤
function parseJSON(str) {
try {
// 嘗試解析JSON(無效JSON會拋出SyntaxError)
const data = JSON.parse(str);
console.log("解析成功:", data);
return data;
} catch (error) {
// 捕獲解析錯誤,輸出信息
console.error("解析失敗:", error.message);
return null; // 出錯時返回默認值
} finally {
// 無論解析成功/失敗,都會執行
console.log("JSON解析操作完成");
}
}
// 測試有效JSON
parseJSON('{"name":"張三","age":28}');
// 輸出:
// 解析成功: { name: '張三', age: 28 }
// JSON解析操作完成
// 測試無效JSON
parseJSON('{name:"張三"}'); // 缺少引號,無效JSON
// 輸出:
// 解析失敗: Unexpected token n in JSON at position 1
// JSON解析操作完成
二、try/catch 的核心特性
1. 捕獲特定類型的錯誤
catch塊可通過判斷error.name區分錯誤類型(如SyntaxError、TypeError、ReferenceError),實現精細化錯誤處理。
function riskyOperation() {
try {
// 模擬不同類型的錯誤
// const num = 1 + undefined; // TypeError
// console.log(undeclaredVar); // ReferenceError
JSON.parse("{invalid json}"); // SyntaxError
} catch (error) {
switch (error.name) {
case "SyntaxError":
console.error("語法錯誤:", error.message);
break;
case "TypeError":
console.error("類型錯誤:", error.message);
break;
case "ReferenceError":
console.error("引用錯誤:", error.message);
break;
default:
console.error("未知錯誤:", error.message);
}
}
}
riskyOperation();
// 輸出:語法錯誤: Unexpected token i in JSON at position 1
2. 手動拋出錯誤(throw)
try塊中可通過throw主動拋出錯誤(自定義錯誤信息 / 對象),由catch塊捕獲處理,實現業務邏輯的錯誤預判。
function validateUser(user) {
try {
if (!user.name) {
// 主動拋出自定義錯誤(字符串類型)
throw "用户名不能為空";
}
if (user.age < 0 || user.age > 120) {
// 主動拋出錯誤對象(更規範)
throw new Error("年齡必須在0-120之間");
}
console.log("用户驗證通過");
} catch (error) {
// 捕獲自定義錯誤
console.error("用户驗證失敗:", error);
}
}
validateUser({ name: "", age: 28 });
// 輸出:用户驗證失敗: 用户名不能為空
validateUser({ name: "張三", age: 150 });
// 輸出:用户驗證失敗: Error: 年齡必須在0-120之間
3. 嵌套 try/catch
try/catch支持嵌套,內層catch可處理內層try的錯誤,未捕獲的錯誤會向上拋給外層catch。
try {
try {
// 內層錯誤
JSON.parse("{invalid}");
} catch (innerError) {
// 內層捕獲語法錯誤,處理後重新拋出
console.error("內層捕獲:", innerError.message);
throw new Error("內層解析失敗,向上拋出");
}
} catch (outerError) {
// 外層捕獲重新拋出的錯誤
console.error("外層捕獲:", outerError.message);
}
// 輸出:
// 內層捕獲: Unexpected token i in JSON at position 1
// 外層捕獲: 內層解析失敗,向上拋出
三、finally 的核心特性
1. 必執行的收尾邏輯
finally塊的核心價值是 “無論如何都執行”,即使try/catch中有return、break、throw等流程控制,也不會跳過。
function testFinally() {
try {
console.log("try塊執行");
return "try返回值"; // 有return,仍會執行finally
} catch (error) {
console.log("catch塊執行");
return "catch返回值";
} finally {
console.log("finally塊執行"); // 必然執行
}
}
console.log(testFinally());
// 輸出:
// try塊執行
// finally塊執行
// try返回值
2. 釋放資源的典型場景
finally常用於釋放資源(如關閉文件、清除定時器、取消網絡請求),避免資源泄漏。
function fetchDataWithTimeout(url) {
let timer;
try {
timer = setTimeout(() => {
throw new Error("請求超時");
}, 5000);
// 模擬接口請求(此處省略真實fetch邏輯)
console.log("發起請求:", url);
// 假設請求成功,清除定時器
clearTimeout(timer);
} catch (error) {
console.error("請求失敗:", error.message);
} finally {
// 無論請求成功/超時,確保清除定時器
clearTimeout(timer);
console.log("定時器已清理");
}
}
fetchDataWithTimeout("https://api.example.com/data");
// 輸出:
// 發起請求: https://api.example.com/data
// 定時器已清理
四、try/catch 的適用場景
1. 處理不可控的外部數據
解析第三方接口返回的 JSON、處理用户輸入的內容時,用try/catch捕獲格式錯誤,避免程序崩潰。
// 處理接口返回的JSON數據
async function handleApiResponse(response) {
try {
const data = await response.json();
return data;
} catch (error) {
console.error("接口數據解析失敗:", error);
// 返回默認數據,保證後續邏輯正常運行
return { code: -1, msg: "數據解析失敗" };
}
}
2. 兼容不同環境的 API
使用非標準 API(如某些瀏覽器專屬方法)時,用try/catch捕獲 “方法不存在” 的錯誤,實現降級處理。
function getLocalStorage(key) {
try {
// 某些環境(如微信小程序)可能無localStorage,或處於隱私模式
return localStorage.getItem(key);
} catch (error) {
console.error("讀取localStorage失敗:", error);
// 降級為cookie存儲
return document.cookie.split(`${key}=`)[1] || null;
}
}
3. 異步代碼的錯誤捕獲
try/catch可捕獲async/await中的異步錯誤(需包裹await語句),替代.catch()鏈式調用,讓異步錯誤處理更直觀。
async function fetchUser() {
try {
const res = await fetch("https://api.example.com/user");
if (!res.ok) {
// 主動拋出HTTP錯誤(狀態碼非2xx)
throw new Error(`請求失敗:${res.status}`);
}
const user = await res.json();
return user;
} catch (error) {
console.error("獲取用户信息失敗:", error.message);
return null;
}
}
五、使用注意事項
1. 避免過度捕獲錯誤
try/catch不應包裹所有代碼,僅用於 “可能出錯且無法提前預判” 的邏輯(如 JSON 解析、網絡請求);若錯誤可通過條件判斷避免(如if (obj) obj.key),則優先用條件判斷,而非try/catch(性能更優)。
// 不推薦:用try/catch捕獲可預判的錯誤
try {
console.log(obj.key);
} catch (error) {
console.log("obj不存在");
}
// 推薦:條件判斷提前規避
if (obj && obj.key) {
console.log(obj.key);
} else {
console.log("obj不存在或key缺失");
}
2. catch 塊必須接收 error 參數
嚴格模式下,catch塊必須聲明錯誤參數(如catch (error)),省略參數會報錯;非嚴格模式下省略參數雖不報錯,但無法獲取錯誤信息,不推薦。
"use strict";
try {
JSON.parse("{invalid}");
} catch { // 錯誤:Strict mode code may not have catch clauses without a parameter
console.log("解析失敗");
}
3. finally 中的 return 會覆蓋 try/catch 的返回值
若finally塊中有return,會覆蓋try/catch中的返回值,導致錯誤信息丟失,開發中需避免這種寫法。
function badFinally() {
try {
return "try返回值";
} finally {
return "finally返回值"; // 覆蓋try的返回值
}
}
console.log(badFinally()); // 輸出:finally返回值
4. 無法捕獲異步回調中的錯誤
try/catch無法捕獲定時器、事件回調、Promise 回調中的錯誤(這些代碼屬於異步任務,執行時已脱離try塊的作用域),需在回調內部單獨捕獲。
// 錯誤示例:無法捕獲setTimeout中的錯誤
try {
setTimeout(() => {
console.log(undeclaredVar); // ReferenceError
}, 100);
} catch (error) {
console.error("捕獲錯誤:", error); // 不會執行
}
// 正確示例:在回調內部捕獲
setTimeout(() => {
try {
console.log(undeclaredVar);
} catch (error) {
console.error("回調內捕獲:", error.message);
}
}, 100);
六、try/catch 與 Promise 錯誤處理的對比
| 場景 | try/catch | Promise.catch() |
|---|---|---|
| 同步代碼 | 適用(首選) | 不適用 |
| async/await 異步代碼 | 適用(直觀) | 適用(鏈式調用) |
| 普通 Promise 回調 | 不適用(無法捕獲) | 適用(首選) |
| 錯誤傳播 | 需手動 throw 向上拋 | 自動向上傳播 |
示例對比:
// 1. async/await + try/catch(推薦)
async function demo1() {
try {
await fetch("https://api.example.com/err");
} catch (error) {
console.error("try/catch捕獲:", error);
}
}
// 2. Promise + catch(傳統寫法)
function demo2() {
fetch("https://api.example.com/err")
.catch(error => console.error("Promise.catch捕獲:", error));
}
try/catch/finally是 JS 錯誤處理的 “標準方案”,核心價值是 “容錯”—— 讓程序在出錯時不崩潰,且能優雅處理錯誤、清理資源。掌握其核心規則(try試錯、catch處理、finally收尾),結合throw主動拋錯、區分錯誤類型,能大幅提升代碼的健壯性;同時需注意異步代碼的錯誤捕獲、避免過度使用try/catch,平衡代碼的容錯性和性能。