JavaScript中的常见错误

目的

鉴于一部分新人不懂得如何根据 JavaScript 中的报错信息来查找错误,本文列举了一些在 JavaScript 中比较常见的错误信息模式,并附上讲解。

希望能帮助新人在 JavaScript 查错方面的能力上有所提升。

提示:
编程并不要求英语能力必须要达到四级、六级甚至更高,但是语言中的关键字以及一些特殊的名称等还是要掌握的。
即使真的英语能力完全不行,但是现在有很多工具可以帮助你。
比如:网易词典、百度/谷歌翻译……
希望大家能善用此类工具,帮助自己。

常见错误

错误的分类

在 JavaScript 中,常见的错误类型有: SyntaxError(语法错误), TypeError(类型错误), ReferenceError(引用错误), RangeError(范围错误) 等。

每个错误信息的提示,开头都是一个未捕获的错误类型,先给出了这是一个什么类型的错误,后续才是这个错误的具体信息。

比如:Uncaught ReferenceError: a is not defined,这就是一个引用错误,说明在使用变量 a 的时候,发现并没有任何声明为 a 的变量。

有时你也会看到没有特别标明错误类型的错误信息,比如: Uncaught Error: this is a test error.

这种错误通常是由开发者自身的代码抛出的未标明具体类型的错误。具体错误原因就需要通过后面的错误信息来分析了。

错误的位置

每个未捕获的错误还会有一个错误的执行堆栈顺序以及代码位置信息。

这些信息会在错误信息的下一行出现,以 at 开头,表明所处位置在哪里。

比如上面提到的错误,实际的完整提示如下:

1
2
Uncaught ReferenceError: a is not defined
at <anonymouse>:1:1

说明该引用错误是在一个匿名【无法追踪到具体环境名称/或者就是一个匿名函数】执行的代码第一行第一列处。

实际上,我就是在浏览器的控制台中直接输出的。

语法错误 SyntaxError

首先明确一下,什么是语法错误?

每一种编程语言都有它的特定语法要求,这是一种规定,必须强制的按照语言的语法来书写代码,否则语言的编译器是会无法识别的;

这种违反了语言的语法来书写出的代码,就属于 语法错误 。

在 JavaScript 中,语法错误会直接导致整个环境的代码完全不执行;

是最重大的错误,同时也是最容易检查出来的错误,只需要随便有一个静态语法检测工具即可检测出来。

错误示例:

1
2
3
lett a = 1; // Uncaught SyntaxError: unexpected identifier
// 本来是想声明一个变量 a 并赋值为 1
// 但是因为错误的书写了 let 关键字,出现了语法错误

类型错误 TypeError

这个错误应该是大多数人经常会看到的,尤其是某个特定的错误信息。

Uncaught TypeError: cannot read property 'XXX' of undefined(或者是 null

怎么样?这个格式的错误信息,是不是很熟悉?
如果你还不懂它到底是什么意思,那么请牢记以下内容。
TypeError 通常用于错误的使用一个 JavaScript 的数据类型。
比如一个变量原本是数字,却被开发者误以为是字符串,并调用了一个字符串的方法【比如 split】,
因为数字是没有这个方法的,所以调用就会出现一个类型错误 TypeError。

那么上面的这个错误信息的解释就是:
在读取属性 XXX 时,发现读取该属性的那个数据是 undefined (或者是 null)。

我们都知道,在 JavaScript 中,undefinednull 都是没有任何属性与方法的,那么把它们认为是一个对象去读取属性时,自然就是错误的。

像这样的类型错误,
要么是开发者没有做好数据的类型判断、空值判断,导致了意外类型数据的异常调用;
要么就是开发者马虎,使用了错误的数据去读取属性或调用方法。

类似的错误信息还有很多种:

1
2
3
4
5
Uncaught TypeError: null is not an object. // 这个跟上面那个属于同一种情况,只是在一些不同的环境下的不同错误信息

Uncaught TypeError: obj.fn is not a function. // 这个通常是调用了一个不存在的方法出现的错误。可能是忘记了添加方法,也可能是 obj 本身就不是开发者期望中的那个数据

Uncaught TypeError: fn is not a function. // 这个类似于上一条 obj.fn,不同的地方在于这里是单独调用 fn,但 fn 实际上并不是一个函数。

要防止这种错误的发生,要怎么做呢?

在使用一个变量、调用一个函数或者访问对象属性时,多考虑考虑它是否可能是其他类型的值,或者是否有可能为空。
多做几个类型判断或者非空判断来尽量避免。

非空判断引申

在做空值判断时,一定要考虑清楚几个特殊的值在实际场景下要归于哪一种情况。
这几个特殊的值分别是:布尔值的 false,空字符 '',数字 0NaN
因为大多数时候,我们去除空值判断时,是采用的取反操作或直接判断是否为真,而上述几个值虽然并非 null 和 undefined,但本身直接转为布尔值都是假值;取反之后,都是真值。

比如:

1
2
3
4
5
6
let n = 0;
if (n) {
console.log('n有值');
} else {
console.log('n没有值');
}

上面这个例子可能会有超出预期的情况,如果数字为 0 需要被认为有值的话,应该再加上一个判断:

1
2
3
4
5
if (n || n === 0) {
console.log('n有值');
} else {
console.log('n没有值');
}

引用错误 ReferenceError

就像最开始解释错误分类时提出的错误示例那样, ReferenceError 就是变量的错误引用所抛出的错误类型。
这通常发生在变量未声明时就被使用的情况下。

比如下面这段代码:

1
2
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 1;

我们知道 ES6 中引入的 let 关键字并不会像 var 那样有变量的声明提升。
let 声明之前就使用 a,会造成一个暂时性死区的问题,具体关于暂时性死区的情况这里不做详谈,这里要讲的是引用错误 ReferenceError。
因为在 let 声明之前,还没有变量 a,所以在访问变量 a 时,编译器就无法找到这个变量对应的引用值,如此导致的就是一个 ReferenceError,即引用错误。

要避免这种错误,就需要注意,每个变量的使用,都要放在声明的后面。

可以通过把变量的声明都放在代码块的一开始来规避。