Skip to content

错误处理 ​

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 块中的代码一旦抛出错误,立即跳转到 catch
  • catch 的参数是错误对象,通常具有 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范围错误,如数组长度超出范围
URIErrorURI 编码/解码错误
EvalErroreval 执行错误
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)
})

最佳实践 ​

  1. 不要吞掉错误 — catch 中至少要打印日志
  2. 区分可恢复和不可恢复错误 — 可恢复的尝试恢复,不可恢复的优雅降级
  3. 使用自定义错误类型 — 方便区分错误来源
  4. 异步操作必须加 catch — 未捕获的 Promise 拒绝会导致进程退出
  5. finally 做清理 — 关闭文件、释放资源等
  6. 不要用 try/catch 包裹不需要捕获的代码 — 只在需要的地方使用