常见错误、调试和错误处理

2018-03-04大约18分钟

在写代码的时候,不可避免地会遇到一些错误,不论初学者还是经验丰富的专家皆如此。尤其是代码行数增长到几百行以后,代码出错的几率就显著增加了,发现这些错误或缺陷的难度也会响应地增加。

本文讨论了一些JavaScript代码中常见的一些错误,了解了这些错误之后,就可以在编码的时候特别留意它们,减少此类错误发生的次数;另外,也介绍了一下调试代码的工具,学习如何使用这些工具单步调试代码,在代码运行时查看变量的内容,从而找出难以发现的错误。

使用未经定义的变量

JavaScript对定义变量的要求非常宽松,因此即使没有使用varletconst等关键字,也可以隐式地创建一个新的全局变量。比如:

a = 10;
console.log(a);

上面代码运行结果不错,但这样来写就不行

a += 10;
console.log(a);

错误发生后,错误信息“ReferenceError: a is not defined” 明确地指出了“a”没有被定义,因此改正起来就比较容易。

如果严格遵守用变量声明关键字来定义变量,尤其是要尽量使用letconst来显式地定义变量,那么在写代码的时候,就很容易避免这类错误。

不区分大小写

JavaScript代码和很多其他编程语言一样,都是对大小写敏感的,因此,编码的时候要特别留意不要弄错大小写。

请找出下面代码中的错误:

let myName = "Tom";

If (myName === "tom") {
    console.log(myName.toUppercase());
}

代码运行出错,对吧?

第一个错:Unexpected token: punc ({),这个错误信息很让人迷糊,不同的JavaScript引擎,报的错可能还不一样。我们知道是第三行代码有错,但是错误信息中完全看不出来。其实,关键的一点是“token”,通常来说,这是因为一些关键词不能被识别,我们仔细看一下,原来是if关键词的第一个字母被大写了,代码编辑器也不能高亮语法了。改正错误,继续运行:

let myName = "Tom";

if (myName === "tom") {
    console.log(myName.toUppercase());
}

第二个错,代码没错,却没有任何输出。这里不是一个语法错误,而是一个逻辑错误,Tom不等于tom,因此判断条件不成立,因此代码不会按照期望的方式区执行。错误原因发现了,改正错误,继续运行:

let myName = "Tom";

if (myName === "Tom") {
    console.log(myName.toUppercase());
}

第三个错出现了:TypeError: myName.toUppercase is not a function。错误里面说,“is not a function",不是个函数,那是属性吗?写个测试代码看看:

let myName = "Tom";
console.log(myName.toUppercase);

输出是undefined,那就是说myName不存在这个属性或方法,查查文档,才发现,原来把toUpperCase()写成了toUppercase()了。修正完错误,代码就可以正常运行了:

let myName = "Tom";

if (myName === "Tom") {
    console.log(myName.toUpperCase());
}

从上面代码可以看出,大小写不注意的话,即使几行代码,就出现了好几个不同类型的错误,的确挺恼人的,这也说明我们写代码的的时候的确需要非常留意大小写的问题。

不匹配的括号

随便开发工具更加强大和智能化,通常在写代码的时候,IDE就会自动补全括号。但是,有些情况下,还是容易出现漏了大括号}或园括号)而没有发觉的情况。

通常情况下,代码排版太乱的时候,很容易出现这样的问题:

const  areEqual = (x, y) => {
if(x<=y)
{if(x===y)
{
return true;
}
return false;
}

const result = areEqual(1,2);
console.log(result);

代码报错:Unexpected token: eof (undefined),到底是什么原因呢?看起来好难找啊!

这时候不管难不难找,把这样的有问题的代码发给别人帮忙找问题,通常别人是一阵烦的——连基本的代码排版都做不好,还写什么代码啊!

怎么办,先把代码排版好,再看看把:

const areEqual = (x, y) => {
    if (x <= y) {
        if (x === y) {
            return true;
        }
        return false;
    }

const result = areEqual(1, 2);
console.log(result);

啊,原来是第8行少了个大括号},很容易,你来改一下上面的代码?

把赋值和相等弄混

不光在JavaScript中,包括很多其他的编程语言,初学者都容易把赋值操作和相等比较操作弄混。

let a = 1;
if (a = 2) {
    console.log("a是2");
} else {
    console.log("a不是2");
}

初看,程序的打印结果应该是“a不是2”,但实际结果却是“a是2”。问题原因,就出在把赋值运算符误用做相等比较运算符了,赋值操作的逻辑判断总是true

上面例子比较简单,容易找到原因,但是由于这种错误不是语法错误而是逻辑错误,在较长的代码中,这个错误就没那么容易发现了。所以,当程序的逻辑出现错误,应该检查一下这种错误,或者通过单步调试代码,才能找到原因。

分不清方法和属性

在JavaScript中,对象的方法,本质上也是对象的一个属性,只不过是一个函数类型的属性。因此,常见到这种代码:

// 假定我们在代码某个地方有这样一个对象,但我们或不容易找到这个代码
const dog = {
    name: "Teddy",
    printName: function() {
        console.log(this.name);
    }
};

if (dog.printName) {  // 简单判断属性存在且不空
    dog.printName();  // 调用对象的方法
}

第9行是用来简单判断printName方法是否存在,所以只判断属性,这时候就不应该在属性名后加()。如果加了圆括号的话,就变成方法调用了。

一般规则是:在执行函数时,应该在函数名的后面加上圆括号;把一个函数传递给另一个函数或属性时,不应该使用圆括号。