在 JavaScript 中,报错(Error)不仅仅是程序的“罢工”,它是一套完整的对象体系。当代码运行出现问题时,JavaScript 引擎会根据问题的本质,抛出对应类型的错误对象。
这些错误都继承自基类 Error。理解它们能让你在调试时迅速定位是逻辑出了问题,还是语法或是内存超限。
常见的内置错误类型
| 错误类型 | 触发场景 | 典型例子 |
|---|---|---|
SyntaxError |
语法错误。代码不符合 JS 规范,解析阶段就报错。 | 变量名命名非法、括号没闭合。 |
ReferenceError |
引用错误。尝试访问一个不存在(未声明)的变量。 | 使用未定义的变量 console.log(a)。 |
TypeError |
类型错误。值的类型不是预期类型。 | 对 null 读取属性、调用一个非函数对象。 |
RangeError |
范围错误。数值超出了允许的范围。 | 数组长度为负数、递归过深导致栈溢出。 |
URIError |
URI 错误。相关编码/解码函数使用不当。 | decodeURI('%')(百分号后缺少字符)。 |
EvalError |
Eval 错误。与 eval() 函数相关的错误。 |
在现代 JS 中已极少见,主要为兼容性存在。 |
AggregateError |
集合错误。当一个操作需要报告多个错误时使用。 | Promise.any() 中所有 Promise 都 rejected。 |
深度解构:为什么会报错?
1. SyntaxError:编译器无法理解你
这是唯一一个在代码执行前就会触发的错误。如果脚本中有语法错误,整段脚本都不会运行。
- 例子:
let 123var = 'error';(变量名不能以数字开头)。
2. ReferenceError:作用域的迷失
当你试图读取一个在当前作用域及其父级作用域都找不到的变量名时,引擎会放弃搜索并抛出此异常。
- 注意:在 ES6 之后,如果你在
let或const声明之前访问该变量,会进入“暂时性死区”,抛出的也是引用错误。
3. TypeError:最常见的“运行时”杀手
这通常意味着你的逻辑在某个环节断了。比如你以为某个 API 返回的是数组,结果它返回了 undefined,而你却尝试对它进行 .map() 操作。
- 现象:
Uncaught TypeError: Cannot read properties of undefined (reading 'xxx')。
4. RangeError:触碰了物理或逻辑极限
最典型的场景是死循环递归。每一个函数调用都会占用一定的栈内存(Stack),当递归没有出口时,栈会被撑破。
- 公式表达:若递归深度 ,则必触发
InternalError(非标)或RangeError。
异常的处理与自定义
除了被动接受系统的报错,我们还可以主动“制造”异常来增强程序的健壮性。
-
抛出异常:你可以使用
throw关键字抛出任何东西,但最佳实践是抛出一个Error实例:throw new Error('自定义的错误信息'); -
捕获异常:通过
try...catch...finally结构。这能让程序在遇到局部错误时不至于崩溃,而是优雅地降级处理。
进阶思考:异步中的报错
需要注意的是,传统的 try...catch 无法捕获异步回调(如 setTimeout)中的错误。对于现代开发,我们更依赖 Promise.catch() 或 async/await 配合 try...catch 来捕获那些“发生在未来”的异常。
你想深入了解如何针对某个特定的报错(比如处理异步请求中的错误)进行更优雅的防御性编程吗?