JavaScript 错误对象完全指南:内置类型解析与自定义异常实战

摘要:

本文系统解析 JavaScript 错误对象体系,深入剖析 7 大内置错误类型的使用场景与差异,通过原型继承实现企业级自定义异常,结合堆栈追踪、错误编码等高级技巧,构建可维护的错误处理策略。


一、Error 对象:错误处理基石

核心结构解析:

javascript 复制代码
class Error {  
  constructor(message) {  
    this.name = "Error";  
    this.message = message || "默认错误";  
    this.stack = ""; // 非标准但广泛支持  
  }  
}  

// 实例属性扩展  
const err = new Error("文件未找到");  
err.file = "config.json";  
err.code = "ENOENT";  

关键特性对比:

特性 描述 浏览器支持 Node.js 支持
name 错误类型标识 全支持 全支持
message 人类可读的错误描述 全支持 全支持
stack 调用堆栈信息 IE10+ 全支持
fileName 引发错误的文件路径 Firefox, Safari
lineNumber 引发错误的行号 Firefox, Safari

堆栈追踪优化技巧:

javascript 复制代码
function captureStack() {  
  const originalPrepare = Error.prepareStackTrace;  
  Error.prepareStackTrace = (_, stack) => stack;  

  const err = new Error();  
  Error.captureStackTrace(err, captureStack); // 跳过当前函数  

  const stack = err.stack;  
  Error.prepareStackTrace = originalPrepare;  

  return stack.map(frame => ({  
    file: frame.getFileName(),  
    line: frame.getLineNumber(),  
    column: frame.getColumnNumber(),  
    function: frame.getFunctionName()  
  }));  
}  

console.log(captureStack());  
// 输出: [{file: "app.js", line: 20, function: "main"}]  

二、七大内置错误类型详解

1. SyntaxError:语法解析错误

javascript 复制代码
// 常见触发场景  
JSON.parse('{name: "Alice"}'); // 缺少双引号  
eval("const 123var = 1;");     // 非法标识符  

诊断技巧

javascript 复制代码
try {  
  // 可能出错的代码  
} catch (err) {  
  if (err instanceof SyntaxError) {  
    console.error("语法错误:", err.message);  
    const match = err.message.match(/at position (\d+)/);  
    if (match) highlightError(match[1]);  
  }  
}  

2. TypeError:类型操作错误

javascript 复制代码
// 典型用例  
null.property;            // 空值访问  
const fn = 123; fn();     // 非函数调用  
Object.freeze({}).prop=1; // 不可写属性赋值  

防御性编程模式

javascript 复制代码
function safeAccess(obj, propChain) {  
  return propChain.split(".").reduce(  
    (acc, prop) => acc?.[prop],  
    obj  
  ) ?? defaultValue;  
}  

safeAccess(user, "address.city"); // 不会抛出TypeError  

3. ReferenceError:引用不存在

javascript 复制代码
// 常见场景  
console.log(notDefined); // 未声明变量  
window.undefinedVar;     // 浏览器全局不存在  

严格模式增强

javascript 复制代码
"use strict";  
undeclaredVar = 10; // 抛出ReferenceError (非严格模式创建全局变量)  

4. RangeError:数值越界

javascript 复制代码
// 触发条件  
new Array(-1);           // 非法数组长度  
(123.45).toFixed(200);   // 超出0-100范围  

边界检查工具函数

javascript 复制代码
function assertRange(value, min, max) {  
  if (value < min || value > max) {  
    throw new RangeError(  
      `值 ${value} 超出范围 [${min}, ${max}]`  
    );  
  }  
}  

assertRange(array.length, 0, MAX_SIZE);  

5. URIError:URI处理错误

javascript 复制代码
decodeURIComponent("%"); // 无效URI序列  
encodeURI("\uD800");     // 代理对错误  

安全URI处理方案

javascript 复制代码
function safeDecode(uri) {  
  try {  
    return decodeURIComponent(uri);  
  } catch (err) {  
    if (err instanceof URIError) {  
      return uri; // 或返回清理后的版本  
    }  
    throw err;  
  }  
}  

6. EvalError:eval执行错误 (已弃用)

javascript 复制代码
// 历史遗留 (ES5+不再抛出)  
try {  
  throw new EvalError("旧式错误");  
} catch (err) {  
  console.warn("兼容性处理:", err.name);  
}  

7. AggregateError:多错误聚合 (ES2021)

javascript 复制代码
const promises = [  
  Promise.reject(new Error("错误1")),  
  Promise.reject(new TypeError("错误2"))  
];  

Promise.any(promises).catch(err => {  
  if (err instanceof AggregateError) {  
    err.errors.forEach((e, i) => {  
      console.error(`错误#${i+1}:`, e.message);  
    });  
  }  
});  

三、自定义错误类型实战

企业级错误基类实现:

javascript 复制代码
class AppError extends Error {  
  constructor(message, code = "GENERIC_ERROR", status = 500) {  
    super(message);  
    this.name = this.constructor.name;  
    this.code = code;  
    this.status = status;  
    this.timestamp = new Date().toISOString();  
    Error.captureStackTrace(this, this.constructor);  
  }  

  toJSON() {  
    return {  
      name: this.name,  
      code: this.code,  
      message: this.message,  
      status: this.status,  
      timestamp: this.timestamp  
    };  
  }  
}  

// 使用示例  
throw new AppError("用户未认证", "AUTH_REQUIRED", 401);  

领域特定错误扩展:

javascript 复制代码
class DatabaseError extends AppError {  
  constructor(message, query) {  
    super(message, "DB_OPERATION_FAILED", 503);  
    this.query = query;  
  }  

  toJSON() {  
    return {  
      ...super.toJSON(),  
      query: this.query  
    };  
  }  
}  

class ValidationError extends AppError {  
  constructor(field, value, rule) {  
    super(`字段 ${field} 验证失败`, "VALIDATION_ERROR", 400);  
    this.details = [{ field, value, rule }];  
  }  

  addError(field, value, rule) {  
    this.details.push({ field, value, rule });  
    return this;  
  }  
}  

// 使用示例  
const err = new ValidationError("email", "invalid", "email格式")  
  .addError("password", "short", "至少8字符");  

console.log(err.toJSON());  
/*  
{  
  name: "ValidationError",  
  code: "VALIDATION_ERROR",  
  message: "字段 email 验证失败",  
  status: 400,  
  details: [  
    { field: "email", value: "invalid", rule: "email格式" },  
    { field: "password", value: "short", rule: "至少8字符" }  
  ]  
}  
*/  

四、错误处理高级模式

1. 错误码标准化体系

javascript 复制代码
// 错误码注册表  
const ERROR_CODES = {  
  // 认证类错误  
  AUTH_REQUIRED: { status: 401, message: "需要认证" },  
  INVALID_TOKEN: { status: 403, message: "无效令牌" },  

  // 数据库错误  
  DB_CONN_FAIL: { status: 503, message: "数据库连接失败" },  

  // 业务规则错误  
  INSUFFICIENT_BALANCE: { status: 400, message: "余额不足" }  
};  

function createError(code, details) {  
  const spec = ERROR_CODES[code];  
  if (!spec) throw new Error(`未知错误码: ${code}`);  

  const err = new AppError(  
    details?.message || spec.message,  
    code,  
    spec.status  
  );  

  if (details) Object.assign(err, details);  
  return err;  
}  

// 使用示例  
throw createError("INSUFFICIENT_BALANCE", {  
  userId: 123,  
  required: 100,  
  actual: 50  
});  

2. 错误恢复策略模式

javascript 复制代码
const strategies = {  
  // 重试策略  
  retry: (error, maxAttempts = 3) => {  
    return function(action) {  
      let attempts = 0;  
      while (attempts < maxAttempts) {  
        try {  
          return action();  
        } catch (err) {  
          if (++attempts >= maxAttempts) throw err;  
          console.warn(`重试中 (${attempts}/${maxAttempts})...`);  
        }  
      }  
    };  
  },  

  // 后备值策略  
  fallback: (value) => () => value,  

  // 断路器模式  
  circuitBreaker: (threshold = 5, timeout = 10000) => {  
    let failures = 0;  
    let lastFailure = 0;  

    return function(action) {  
      if (failures >= threshold && Date.now() - lastFailure < timeout) {  
        throw new Error("服务熔断中");  
      }  

      try {  
        const result = action();  
        failures = 0; // 重置计数器  
        return result;  
      } catch (err) {  
        failures++;  
        lastFailure = Date.now();  
        throw err;  
      }  
    };  
  }  
};  

// 使用示例  
const fetchWithRetry = strategies.retry(new Error("超时"), 2);  
fetchWithRetry(() => fetch("/api"));  

3. 错误监控集成

javascript 复制代码
class ErrorMonitor {  
  constructor(service) {  
    this.service = service;  
    this.breadcrumbs = [];  
  }  

  recordBreadcrumb(message) {  
    this.breadcrumbs.push({  
      message,  
      timestamp: Date.now(),  
      context: { url: location.href }  
    });  
  }  

  capture(err, level = "error") {  
    const report = {  
      error: {  
        name: err.name,  
        message: err.message,  
        stack: err.stack  
      },  
      level,  
      breadcrumbs: this.breadcrumbs,  
      context: {  
        appState: store.getState(),  
        user: currentUser  
      }  
    };  

    this.service.send(report);  
    this.breadcrumbs = []; // 清空面包屑  
  }  

  wrap(fn) {  
    return (...args) => {  
      try {  
        return fn(...args);  
      } catch (err) {  
        this.capture(err);  
        throw err;  
      }  
    };  
  }  
}  

// 集成示例  
const monitor = new ErrorMonitor(SentryService);  
monitor.recordBreadcrumb("用户点击支付按钮");  

const safeHandler = monitor.wrap(function() {  
  throw new Error("支付失败");  
});  

button.addEventListener("click", safeHandler);  

五、生产环境最佳实践

1. 错误分类处理策略

2. 安全错误信息策略

javascript 复制代码
// 开发环境显示完整错误  
function devError(err) {  
  return {  
    status: err.status,  
    error: err.name,  
    message: err.message,  
    stack: err.stack,  
    details: err.details  
  };  
}  

// 生产环境过滤敏感信息  
function prodError(err) {  
  const safeResponse = {  
    status: err.status,  
    code: err.code  
  };  

  // 保留业务安全消息  
  if (err.isOperational) {  
    safeResponse.message = err.message;  
  } else {  
    safeResponse.message = "系统内部错误";  
  }  

  return safeResponse;  
}  

// Express中间件  
app.use((err, req, res, next) => {  
  const isProd = process.env.NODE_ENV === "production";  
  res.status(err.status || 500)  
     .json(isProd ? prodError(err) : devError(err));  
});  

3. 错误生命周期管理

javascript 复制代码
// 错误处理流水线  
function handleError(err) {  
  // 步骤1: 丰富上下文  
  enrichError(err);  

  // 步骤2: 分类处理  
  if (err instanceof NetworkError) {  
    return handleNetworkError(err);  
  }  
  if (err instanceof BusinessError) {  
    return handleBusinessError(err);  
  }  

  // 步骤3: 监控上报  
  errorMonitor.capture(err);  

  // 步骤4: 恢复或终止  
  if (isRecoverable(err)) {  
    return recoveryProcedure();  
  }  
  terminateApplication();  
}  

// 未捕获错误的最后防线  
process.on("uncaughtException", err => {  
  handleError(err);  
  if (!isRecoverable(err)) process.exit(1);  
});  

结语

JavaScript 错误处理是构建健壮应用的核心技能,本文涵盖:

  1. 7 大内置错误类型的深度解析
  2. 企业级自定义错误实现方案
  3. 错误码标准化与恢复策略
  4. 生产环境监控与安全实践

关键认知:优秀错误处理不仅是技术实现,更是用户体验的核心组成。当错误信息能帮助用户明确下一步行动时,系统才真正实现韧性设计。

本文是 JavaScript 错误处理领域的终极指南,如果对您有帮助,请点赞收藏支持!关注作者获取更多工程化深度内容。

相关推荐
摸鱼仙人~37 分钟前
styled-components:现代React样式解决方案
前端·react.js·前端框架
sasaraku.1 小时前
serviceWorker缓存资源
前端
RadiumAg2 小时前
记一道有趣的面试题
前端·javascript
yangzhi_emo2 小时前
ES6笔记2
开发语言·前端·javascript
yanlele3 小时前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子4 小时前
React状态管理最佳实践
前端
烛阴4 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子4 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...4 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
初遇你时动了情4 小时前
腾讯地图 vue3 使用 封装 地图组件
javascript·vue.js·腾讯地图