从浏览器的控制台到运行 Node.js 的计算机终端,我们到处都会看到各类错误。
这篇文章的重点是概述我们在 JS 开发过程中可能遇到的错误类型。
**提示:**良好的错误会导致快速而无痛的发展经历与缓慢而令人难受的发展经历之间的区别。在编写可重用的代码时,请确保您编写清晰易懂的错误。
当数字超出允许的值范围时,将抛出此错误。
例如,
const l = console.log
const arr = \[90,88\]
arr.length=90**99
我们有一个带有两个元素的 arr。接下来,我们尝试使数组包含90**99 == 2.9512665430652753e+193
元素。
这个数字超出了大小数组可以增长的范围。所以运行时它会抛出 RangeError:
$ node errors
errors.js:4
arr.length=90**99
^
RangeError: Invalid array length
因为我们要增加 arr 数组的数量超出了 JS 指定的范围。
当对变量 /项的引用被破坏或不存在时,将引发此错误。也就是说,变量 /项不存在。
例如,
const l=console.log
const cat = "cat"
cat
dog
我们有一个变量 cat 初始化为“ cat”。接下来,我们引用 cat 变量和 dog 变量。cat 变量存在,而 dog 变量不存在。
cat 将返回“ cat”,而 dog 会引发 ReferenceError,因为在环境记录中找不到名为 dog 的变量。
$ node errors
errors.js:3
dog
^
ReferenceError: dog is not defined
每当我们创建或定义变量时,变量名称都会写入环境记录中。此环境记录就像键值存储表一样,如下图:
+-------------+
| Key | Value |
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
| cat | "cat" |
+-------------+
每当我们引用变量时,它都会存储程序中定义的变量。当在记录中找到环境值并提取并返回值时,将以该变量的名称作为关键字搜索环境记录。调用尚未定义的函数。
现在,当我们创建或定义一个没有赋值的变量时。变量将键作为变量名写入环境记录,但该值将保持未定义状态。
var cat
env record
+-----------------+
| Key | Value |
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
| cat | undefined |
+-----------------+
稍后为变量分配值时,将在 env 记录中搜索该变量,当发现该初始未定义值时,该赋值将被覆盖。
var cat
cat = "cat"
env record
+-------------+
| Key | Value |
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
| cat | "cat" |
+-------------+
因此,当在 env 记录中找不到变量名时,JS 引擎会抛出 RefernceError。
+-------------+
| Key | Value |
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
| cat | "cat" |
+-------------+
cat // "cat", yes, :) it's there
dog // :( what's this? can't find it
注意:未定义的变量不会抛出 ReferenceError,因为它存在于环境记录中只是它的值尚未设置。
这是我们遇到的最常见的错误。当我们键入 JS 引擎难以理解的代码时,会出现此错误。解析期间,JS 引擎捕获了此错误。
在 JS 引擎中,我们的代码经历了不同的阶段,然后才能在终端上看到运行结果。
标记化将源代码分解为各个单元。在这个阶段,将对数字,关键字,文字,运算符进行分类并分别进行标记。接下来,生成的 token 流将传递到解析阶段,由解析器处理。这是从 token 生成 AST 的地方。AST 是我们代码结构的抽象数据结构。
在标记化和解析这两个阶段,如果我们代码的语法不符合 JS 的语法规则,则会使执行阶段失败并引发 SyntaxError。例如,
const l = console.log
let cat h =“ cat”
这里的“h”明显是多余的,所以由于多了这个字符,会导致引擎抛出 SyntaxError
$ node errors
errors.js:3
let cat h = "cat"
^
SyntaxError: Unexpected identifier
很显然,Node.js 引擎发现了错误,由于这个不和谐字符的出现,导致 cat 变量的声明失败了。
TypeError 是指对象用来表示值的类型非预期类型时发生的错误。例如,我们期望它是布尔值,但结果发现它是 string 类型。
再例如:
const num = 123
num.toUpperCase()
这会引发 TypeError
$ node errors
errors.js:4
num.toUpperCase()
^
TypeError: num.toUpperCase is not a function
因为 toUpperCase 函数需要字符串数据类型。toUpperCase 函数是有意通用的;它不需要其 this 值是 String 对象。因此,可以将其转移到其他种类的对象中用作方法。
如果我们在 Objects,Boolean,Symbol,null,undefined 数据类型上调用 toUpperCase 函数,则只有字符串会转换为大写或小写形式,我们将得到 TypeError,因为它操作的数据类型错误。
这说明了使用一种全局 URI 处理功能与其定义不兼容。
JS 中的 URI (统一资源指示符)具有以下功能:decodeURI,decodeURIComponent 等。
如果我们用错误的参数调用其中任何一个,我们将得到一个 URIError。
decodeURI("%")
^
URIError: URI malformed
encodeURI,获取 URI 的未编码版本。“%”不是正确的 URI,因此引发了 URIError。
编码或解码 URI 时出现问题时,将引发 URIError。
如果非法调用 eval(),则抛出 EvalError 异常。
根据 EcmaSpec 2018 版:
此异常不再会被 JavaScript 抛出,但是 EvalError 对象仍然保持兼容性。
该错误在 JS 引擎内部发生,特别是当它有太多数据要处理并且堆栈增长超过其关键限制时。
当 JS 引擎被太多的递归,太多的切换情况等淹没时,就会发生这种情况
switch(num) {
case 1:
...
break
case 2:
...
break
case 3:
...
break
case 4:
...
break
case 5:
...
break
case 6:
...
break
case 7:
...
break
... up to 1000 cases
}
太多的递归,一个简单的例子是这样的:
function foo() {
foo()
}
foo()
正如我们所说,没有人能不犯错误。就我们输入的代码而言,发生错误是难以避免的。不过为了避免更多的错误出现,我们需要知道抛出的错误的类型是什么,我们该如何解决。
所以我们在这篇文章中列出了它们,并提供了一些示例来简要的来介绍了它们是如何发生的。
最后,希望本文的一些浅见能为你写出更好的代码提供一些帮助,谢谢!