错误处理
try/catch/finally
用于捕获和处理运行时错误,防止程序崩溃。
基本语法
js
try {
// 可能会抛出错误的代码
const result = riskyOperation()
console.log(result)
} catch (error) {
// 错误处理逻辑
console.error("发生错误:", error.message)
} finally {
// 无论是否出错都会执行(可省略)
console.log("清理资源")
}try {
// 可能会抛出错误的代码
const result = riskyOperation()
console.log(result)
} catch (error) {
// 错误处理逻辑
console.error("发生错误:", error.message)
} finally {
// 无论是否出错都会执行(可省略)
console.log("清理资源")
}try/catch 特点
try块中的代码一旦抛出错误,立即跳转到catchcatch的参数是错误对象,通常具有name和message属性finally会在 try/catch 之后执行,即使有 return 也会执行finally中的 return 会覆盖 try 或 catch 中的 return
js
function foo() {
try {
return "try"
} finally {
return "finally"
}
}
foo() // "finally"function foo() {
try {
return "try"
} finally {
return "finally"
}
}
foo() // "finally"嵌套 try/catch
js
try {
try {
throw new Error("内部错误")
} catch (e) {
console.log("内部 catch:", e.message)
throw e // 重新抛出
}
} catch (e) {
console.log("外部 catch:", e.message)
}try {
try {
throw new Error("内部错误")
} catch (e) {
console.log("内部 catch:", e.message)
throw e // 重新抛出
}
} catch (e) {
console.log("外部 catch:", e.message)
}throw
throw 用于手动抛出错误,可以抛出任意类型的值,但建议抛出 Error 对象。
js
function divide(a, b) {
if (b === 0) {
throw new Error("除数不能为零")
}
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("参数必须是数字")
}
return a / b
}function divide(a, b) {
if (b === 0) {
throw new Error("除数不能为零")
}
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("参数必须是数字")
}
return a / b
}Error 类型
| 错误类型 | 说明 |
|---|---|
Error | 通用错误基类 |
SyntaxError | 语法错误(仅在开发阶段出现) |
TypeError | 类型错误,如调用非函数、访问 undefined 的属性 |
ReferenceError | 引用错误,访问不存在的变量 |
RangeError | 范围错误,如数组长度超出范围 |
URIError | URI 编码/解码错误 |
EvalError | eval 执行错误 |
js
try {
undefined.name // TypeError: Cannot read properties of undefined
} catch (e) {
console.log(e instanceof TypeError) // true
console.log(e.name) // "TypeError"
console.log(e.message) // "Cannot read properties of undefined..."
console.log(e.stack) // 调用堆栈信息(非标准但广泛支持)
}try {
undefined.name // TypeError: Cannot read properties of undefined
} catch (e) {
console.log(e instanceof TypeError) // true
console.log(e.name) // "TypeError"
console.log(e.message) // "Cannot read properties of undefined..."
console.log(e.stack) // 调用堆栈信息(非标准但广泛支持)
}自定义错误
js
class ValidationError extends Error {
constructor(message, field) {
super(message)
this.name = "ValidationError"
this.field = field
}
}
class NetworkError extends Error {
constructor(message, statusCode) {
super(message)
this.name = "NetworkError"
this.statusCode = statusCode
}
}
// 使用
try {
throw new ValidationError("用户名不能为空", "username")
} catch (e) {
if (e instanceof ValidationError) {
console.log(`字段 ${e.field} 验证失败: ${e.message}`)
} else if (e instanceof NetworkError) {
console.log(`网络错误,状态码: ${e.statusCode}`)
} else {
console.log("未知错误:", e)
}
}class ValidationError extends Error {
constructor(message, field) {
super(message)
this.name = "ValidationError"
this.field = field
}
}
class NetworkError extends Error {
constructor(message, statusCode) {
super(message)
this.name = "NetworkError"
this.statusCode = statusCode
}
}
// 使用
try {
throw new ValidationError("用户名不能为空", "username")
} catch (e) {
if (e instanceof ValidationError) {
console.log(`字段 ${e.field} 验证失败: ${e.message}`)
} else if (e instanceof NetworkError) {
console.log(`网络错误,状态码: ${e.statusCode}`)
} else {
console.log("未知错误:", e)
}
}Promise 中的错误处理
catch 方法
js
fetch("/api/data")
.then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.error("请求失败:", err))fetch("/api/data")
.then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.error("请求失败:", err))async/await 中的错误处理
推荐使用 try/catch 包裹:
js
async function fetchData() {
try {
const res = await fetch("/api/data")
if (!res.ok) {
throw new Error(`HTTP 错误: ${res.status}`)
}
return await res.json()
} catch (error) {
console.error("获取数据失败:", error)
throw error // 可以选择重新抛出
}
}async function fetchData() {
try {
const res = await fetch("/api/data")
if (!res.ok) {
throw new Error(`HTTP 错误: ${res.status}`)
}
return await res.json()
} catch (error) {
console.error("获取数据失败:", error)
throw error // 可以选择重新抛出
}
}Promise 中的 try/catch 也可捕获异步错误
js
async function foo() {
throw new Error("async 中的错误")
}
foo().catch((e) => console.log(e.message))async function foo() {
throw new Error("async 中的错误")
}
foo().catch((e) => console.log(e.message))全局错误捕获
浏览器环境
js
// 同步错误
window.onerror = function (message, source, lineno, colno, error) {
console.error("全局错误:", message)
return true // 阻止浏览器默认错误处理
}
// 未捕获的 Promise 错误
window.addEventListener("unhandledrejection", (event) => {
console.error("未处理的 Promise 拒绝:", event.reason)
event.preventDefault()
})// 同步错误
window.onerror = function (message, source, lineno, colno, error) {
console.error("全局错误:", message)
return true // 阻止浏览器默认错误处理
}
// 未捕获的 Promise 错误
window.addEventListener("unhandledrejection", (event) => {
console.error("未处理的 Promise 拒绝:", event.reason)
event.preventDefault()
})Node.js 环境
js
process.on("uncaughtException", (err) => {
console.error("未捕获的异常:", err)
process.exit(1) // 建议退出进程
})
process.on("unhandledRejection", (reason, promise) => {
console.error("未处理的 Promise 拒绝:", reason)
})process.on("uncaughtException", (err) => {
console.error("未捕获的异常:", err)
process.exit(1) // 建议退出进程
})
process.on("unhandledRejection", (reason, promise) => {
console.error("未处理的 Promise 拒绝:", reason)
})最佳实践
- 不要吞掉错误 — catch 中至少要打印日志
- 区分可恢复和不可恢复错误 — 可恢复的尝试恢复,不可恢复的优雅降级
- 使用自定义错误类型 — 方便区分错误来源
- 异步操作必须加 catch — 未捕获的 Promise 拒绝会导致进程退出
- finally 做清理 — 关闭文件、释放资源等
- 不要用 try/catch 包裹不需要捕获的代码 — 只在需要的地方使用
Cao Note