前言
在使用 JavaScript 进行开发过程中,错误是难以避免的。当我们编写的代码违反了语法规则、引用未定义的变量、错误地操作数据类型或超出有效范围时,JavaScript 会抛出各种错误。这些错误不仅会导致代码执行失败,还可能引起不可预料的问题,给开发者带来不便和困扰。
了解 JavaScript 中常见的错误类型及相应的解决方法,对于快速定位和修复问题至关重要。本篇文章将介绍一些常见的 JavaScript 错误类型,帮助开发者更好地理解错误,进而更好的处理错误。
1. 错误概述
JavaScript 中的错误是一个对象,在发生错误时会抛出该对象以停止程序。在 JavaScript 中,可以通过构造函数来创建一个新的通用错误:
const err = new Error("Error");
当然,也可以省略 new 关键字:
const err = Error("Error");
Error 对象有三个属性:
-
message
:带有错误消息的字符串; -
name
: 错误的类型; -
stack
:函数执行的堆栈跟踪。
例如,创建一个 TypeError 对象,该消息将携带实际的错误字符串,其 name 将是“TypeError”:
const wrongType = TypeError("Expected number");
wrongType.message; // 'Expected number'
wrongType.name; // 'TypeError'
堆栈跟踪是发生异常或警告等事件时程序所处的方法调用列表:
fileOf7174.png
它首先会打印错误名称和消息,然后是被调用的方法列表。每个方法调用都说明其源代码的位置和调用它的行。可以使用此数据来浏览代码库并确定导致错误的代码段。此方法列表以堆叠的方式排列。它显示了异常首先被抛出的位置以及它如何通过堆栈方法调用传播。为异常实施捕获不会让它通过堆栈向上传播并使程序崩溃。
对于 Error 对象,Firefox 还实现了一些非标准属性:
-
columnNumber
:错误所在行的列号; -
filename
:发生错误的文件 -
lineNumber
:发生错误的行号
fileOf7174.png
2. 错误类型
JavaScript 中有一系列预定义的错误类型。只要使用者没有明确处理应用程序中的错误,它们就会由 JavaScript 运行时自动选择和定义。
fileOf7174.png
如上图所示,JavaScript 中的错误类型包括:
-
EvalError
-
InternalError
-
RangeError
-
ReferenceError
-
SyntaxError
-
TypeError
-
URIError
这些错误类型都是实际的构造函数,旨在返回一个新的错误对象。最常见的就是 TypeError。大多数时候,大部分错误将直接来自 JavaScript 引擎,例如 InternalError 或 SyntaxError。
JavaScript 提供了 instanceof
运算符可以用于区分异常类型:
try {
If (typeof x !== ‘number’) {
throw new TypeError(‘x 应是数字’);
} else if (x <= 0) {
throw new RangeError('x 应大于 0');
} else {
// ...
}
} catch (err) {
if (err instanceof TypeError) {
// 处理 TypeError 错误
} else if (err instanceof RangeError) {
// 处理 RangeError 错误
} else {
// 处理其他类型错误
}
}
下面来了解 JavaScript 中最常见的错误类型,并了解它们发生的时间和原因。
(1)SyntaxError
SyntaxError 表示语法错误。这些错误是最容易修复的错误之一,因为它们表明代码语法中存在错误。由于 JavaScript 是一种解释而非编译的脚本语言,因此当应用程序执行包含错误的脚本时会抛出这些错误。在编译语言的情况下,此类错误在编译期间被识别。因此,在修复这些问题之前,不会创建应用程序二进制文件。
SyntaxError 发生的一些常见原因是:
-
缺少引号
-
缺少右括号
-
大括号或其他字符对齐不当
fileOf7174.png
(2)TypeError
TypeError 是 JavaScript 应用程序中最常见的错误之一,当某些值不是特定的预期类型时,就会产生此错误。
fileOf7174.png
TypeError 发生的一些常见原因是:
-
调用不是方法的对象。
-
试图访问 null 或未定义对象的属性
-
将字符串视为数字,反之亦然
(3)ReferenceError
ReferenceError 表示引用错误。当代码中的变量引用有问题时,会发生 ReferenceError。可能忘记在使用变量之前为其定义一个值,或者可能试图在代码中使用一个不可访问的变量。在任何情况下,通过堆栈跟踪都可以提供充足的信息来查找和修复有问题的变量引用。
ReferenceErrors 发生的一些常见原因如下:
-
在变量名中输入错误。
-
试图访问其作用域之外的块作用域变量。
-
在加载之前从外部库引用全局变量。
fileOf7174.png
(4)RangeError
RangeError 表示范围错误。当变量设置的值超出其合法值范围时,将抛出 RangeError。它通常发生在将值作为参数传递给函数时,并且给定值不在函数参数的范围内。当使用记录不完整的第三方库时,有时修复起来会很棘手,因为需要知道参数的可能值范围才能传递正确的值。
RangeError 发生的一些常见场景如下:
-
试图通过 Array 构造函数创建非法长度的数组。
-
将错误的值传递给数字方法,例如
toExponential()
、toPrecision()
、toFixed()
等。 -
将非法值传递给字符串函数,例如
normalize()
。
fileOf7174.png
(5)URIError
URIError 表示 URI 错误。当 URI 的编码和解码出现问题时,会抛出 URIError。JavaScript 中的 URI 操作函数包括:decodeURI
、decodeURIComponent
等。如果使用了错误的参数(无效字符),就会抛出 URIError。
fileOf7174.png
(6)EvalError
EvalError 表示 Eval 错误。当 eval()
函数调用发生错误时,会抛出 EvalError。不过,当前的 JavaScript 引擎或 ECMAScript 规范不再抛出此错误。但是,为了向后兼容,它仍然是存在的。
如果使用的是旧版本的 JavaScript,可能会遇到此错误。在任何情况下,最好调查在 eval()函数调用中执行的代码是否有任何异常。
(7)InternalError
InternalError 表示内部错误。在 JavaScript 运行时引擎发生异常时使用。它表示代码可能存在问题也可能不存在问题。
InternalError 通常只发生在两种情况下:
-
当 JavaScript 运行时的补丁或更新带有引发异常的错误时(这种情况很少发生);
-
当代码包含对于 JavaScript 引擎而言太大的实体时(例如,数组初始值设定项太大、递归太多)。
解决此错误最合适的方法就是通过错误消息确定原因,并在可能的情况下重构应用逻辑,以消除 JavaScript 引擎上工作负载的突然激增。
注意: 现代 JavaScript 中不会抛出 EvalError 和 InternalError。
(8)创建自定义错误类型
虽然 JavaScript 提供了足够的错误类型类列表来涵盖大多数情况,但如果这些错误类型不能满足要求,还可以创建新的错误类型。这种灵活性的基础在于 JavaScript 允许使用 throw 命令抛出任何内容。
可以通过扩展 Error 类以创建自定义错误类:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
可以通过以下方式使用它:
throw ValidationError("未找到该属性: name")
可以使用 instanceof
关键字识别它:
try {
validateForm() // 抛出 ValidationError 的代码
} catch (e) {
if (e instanceof ValidationError) {
}
else {
}
}
3. 抛出错误
很多人认为错误和异常是一回事。实际上,Error 对象只有在被抛出时才会成为异常。
在 JavaScript 中抛出异常,可以使用 throw 来抛出 Error 对象:
throw TypeError("Expected number");
或者:
throw new TypeError("Expected number");
来看一个简单的例子:
function toUppercase(string) {
if (typeof string !== "string") {
throw TypeError("Expected string");
}
return string.toUpperCase();
}
在这里,我们检查函数参数是否为字符串。如果不是,就抛出异常。
从技术上讲,我们可以在 JavaScript 中抛出任何东西,而不仅仅是 Error 对象:
throw Symbol();
throw 33;
throw "Error!";
throw null;
但是,最好避免这样做:要抛出正确的 Error 对象,而不是原语。
4. 抛出异常时会发生什么?
异常一旦抛出,就会在程序堆栈中冒泡,除非在某个地方被捕获。
来看下面的例子:
function toUppercase(string) {
if (typeof string !== "string") {
throw TypeError("Expected string");
}
return string.toUpperCase();
}
toUppercase(4);
在浏览器或 Node.js 中运行此代码,程序将停止并抛出错误:
fileOf7174.png
这里还显示了发生错误的确切行。这个错误就是一个堆栈跟踪,有助于跟踪代码中的问题。堆栈跟踪从下到上:
at toUppercase (<anonymous>:3:11)
at <anonymous>:9:1
toUppercase 函数在第 9 行调用,在第 3 行抛出错误。除了在浏览器的控制台中查看此堆栈跟踪之外,还可以在 Error 对象的 stack
属性上访问它。
总结
本篇文章主要介绍了关于 JavaScript 中错误的基础知识,只有更好的理解它们已经了解这些错误发生的场景,我们才会提前规避一些错误的发生,提高代码的健壮性,接下来的文章我们将继续学习同步和异步 JavaScript 代码中的错误和异常处理。