一旦你发现一个错误,就可以清除它。不幸的是,发现它们的
出处并不总是很容易 - 你的大部分调试时间只是花在指出错
误的位置。
最可靠的方法之一是在你的代码中加入一些简单的语句打印出
正在发生什么。假如你在下面的两段程序中发现一个问题:
function getName()
{
var first_name = prompt("what's your first name?","");
var last_name = prompt("what's your last name?","");
var the_name = first_name + " " + last_name;
}
function theGreeting()
{
var the_name = "";
the_name = getName();
if (the_name == "Dave Thau")
{
alert("Hello, oh greatest one!");
} else {
alert("Ahoy palloi!");
}
}
运行这段程序,看看你是否能发现出了什么问题(Netscape 3.x
的用户可能会遇到一些错误检查的问题,这是由于Netscape 3.x
本身的原因,以下的Javascript例子与此相同)。如果你在警告
对话框中随意输入一些名字,你会得到问候:“Ahoy palloi!”。
但是,如果你在第一个提示对话框中输入“Dave”,在第二个中
输入“Thau”,你应该得到“Hello,oh greatest one!”这条
信息。然而,你还是只得到“Ahoy palloi!”这条信息。很明显,
函数中出了错误。在这个简单的例子程序中,你或许只是查看
JavaScript代码就能发现错误。然而,当你的代码变得越来越复
杂时,只靠目测来发现错误会变得愈加困难。
如果JavaScript没能捕获你的错误,你也没有通过查看代码发现
错误,有时打印出变量会对你有所帮助。最简单的方法是象下面
这样使用一个alert():
// theGreeting gets a name using getName, then presents
// one or two alert boxes depending on what the name is
//function getName()
{
var first_name = prompt("what's your first name?","");
var last_name = prompt("what's your last name?","");
var the_name = first_name + " " + last_name;
alert("in getName, the_name is: " + the_name);
}
// theGreeting gets a name using getName, then presents
// one of two alert boxes depending on what the name is
// function theGreeting()
{
var the_name = "";
the_name = getName();
alert("after getName, the_name = " + the_name);
if (the_name == "Dave Thau")
{
alert("hello, oh greatest one!");
}else{
alert("ahoy palloi!");
}
}
请注意我们已经在所有重要的地方加入警告语句。现在试着运行
这段程序。如果你输入名称“Dave”和“Thau”,你会注意到第
一个警告显示“in getName, the_name is: Dave Thau,”,但
是第二个警告显示“after getName, the_name = undefined,”,
这就告诉你在getName()的最后一行事情变得糟糕起来。不知何
故,the_name只在函数存在前正确,但是theGreeting没有给变
量the_name正确赋值。当你写的函数能正确运行,但返回值出现
问题时,你最先要做的就是检查你是否的确让其返回了一个值。
很明显,问题就出在这儿。getName()函数指出了名称,但没有
返回它。所以我们应把语句“return the_name;”加到函数末尾。
把一些警告对话框加入你的代码中是很有帮助的。不幸的是,每
隔一行就按一次“OK”也是一种痛苦。
不用警告对话框也能调试代码。一种选择是把调试信息写到窗体
的一个文本区内。另一种可能是把调试信息写在另一个窗口上。
这儿有一个把调试信息写在下面文本区的调试代码的例子。
使你的调试经历更舒适的第三个诀窍是这样的:创建不同的调试
等级,然后设置“调试”变量。下面就是在此页上运行的
JavaScript代码:
多数错误只是无聊的语法错误。记住关闭那些引号,大括号和
小括号会花费很长时间,不过幸运的是JavaScript自动错误检
测器能捕获大部分此类错误。虽然JavaScript错误检测器随着
日渐复杂的流览器而不断完善,但是一些错误仍会溜走。下面
是一些需要留意的常见错误:
混淆变量名或函数名
大写和复数变量和函数名产生的错误令人烦恼地经
常出现,有时JavaScript错误检测器不能捕获它们。
通过建立和坚持使用一种对变量和函数的命名协定,
会大大减少这些麻烦的数量。例如,我全部用小写
字母定义变量,并用下划线代替空格 (my_variable,
the_data, an_example_variable),用内置符号表
示函数 (addThreeNumbers(), writeError()等)。
我避免使用任何复数,因为我总是忘记那些变量是
不是复数。
偶然地使用了保留字
一些字不能作为变量名,因为它们已经被JavaScript
使用。例如,不能定义一个叫“if”的变量,因为
它实际上是JavaScript的一部分 - 如果使用“if”,
你会遇到各种麻烦。当你因为使用命名为“if”的
变量而变得疯狂时,一个叫做“document”的变量
是很诱人的。不幸的是,“document”是一个
JavaScript对象。另一个经常遇到的问题是把变量
命名为“name”(窗体元素有“names”属性)。把
变量命名为“name”不会总出问题,只是有时 -
这会更使人迷惑 - 这就是避免使用“name”变量的
原因。
不幸的是,不同的流览器有不同的保留字,所以没
有办法知道该回避哪些字。最安全的办法是避免使
用已经成为JavaScript一部分的字和HTML使用的字。
如果你因为变量遇到问题,并且不能发现哪儿错了,
试着把变量改个名字。如果成功了,你或许就避开
了保留字。
记住在逻辑判断时应该用两个等号
一些流览器能捕获这种错误,有些却不能。这是一
种非常常见的错误,但是如果流览器不能替你指
出来,你就很难发现。下面是一个这种错误的例子:
var the_name = prompt("what's your name?", "");
if (the_name = "the monkey")
{
alert("hello monkey!");
} else {
alert("hello stranger.");
}
这段代码将产生“hello monkey!”警告对话框 -
不管你在提示里敲的是什么 - 这不是我们希望的。
原因是在if-then语句中只有一个等号,这句话告
诉JavaScript你想让一件事等于另一件。假设你在
提示中敲的是“robbie the robot”。最开始,变
量the_name的值是“robbie the robot”,但是随
后if语句告诉JavaScript你想把the_name设为
“the monkey.”。于是JavaScript很高兴地执行你
的命令,送一个“true”消息给if-then语句,结果
警告对话框每次都出现“hello monkey!”。
这种阴险的错误会使你发疯,所以注意使用两个
等号。
偶然给变量加上了引号,或忘了给字符串加引号
我不时遇到这个问题。JavaScript区分变量和字符
串的唯一方法是:字符串有引号,变量没有。下面
有一个明显的错误:
var the_name = 'koko the gorilla';
alert("the_name is very happy");
虽然the_name是一个变量,但是程序还会产生一个
提示“the_name is very happy,”的警告对话框。
这是因为一旦JavaScript看见引号包围着某些东西
就不再考虑它,所以当你把the_name放在引号里,
你就阻止了JavaScript从内存中查找它。
下面是一个不太明显的此类错误的扩展(我们已经
在第三天的课程里见过):
function wakeMeIn3()
{
var the_message = "Wake up! Hey! Hey! WAKE UP!!!!";
setTimeout("alert(the_message);", 3000);
}
这里的问题是你告诉JavaScript三秒后执行alert
(the_message)。但是,三秒后the_message将不再
存在,因为你已经退出了函数。这个问题可以这样
解决:
function wakeMeIn3()
{
var the_message = "Wake up!";
setTimeout("alert('" + the_message+ "');", 3000);
}
把the_message放在引号外面,命令“alert('Wake
up!');”由setTimeout预定好,就可以得到你想
要的。
这只是一些可能在你的代码中作祟的很难调试的
错误。一旦发现了它们,就有不同的或好或差的方
法来改正错误。你很幸运,因为你能从我的经验和
错误中获益
编好程序的关键是程序是写给人的,不是写给计算机的。如
果你能明白其他人或许会阅读你的JavaScript,你就会写更
清晰的代码。代码越清晰,你就越不容易犯错误。机灵的代
码是可爱的,但就是这种机灵的代码会产生错误。最好的经
验法则是KISS,即Keep It Simple,Sweetie(保持简单,可爱)。
另一个有帮助的技术是在写代码之前作注释。这迫使你在动
手之前先想好。一旦写好了注释,你就可以在其下面写代码。
下面是一个用这种方法写函数的例子:
第一步:写注释
//function beSassy()
// beSassy asks for a user's name, chooses a random
// insult and returns an alert box with the user's name and the
// insult.
function beSassy()
{
// first write a list of insults
//
// next get the user's name
//
// then choose a random insult
//
// finally, return the personalized sass
//
}
第二步:填充代码
//function beSassy()
// beSassy asks for a user's name, chooses a random
// insult and returns an alert box with the user's name and the
// insult.
function beSassy()
{
// first write a list of insults
//
var the_insult_list = new Array;
the_insult_list[0] = "your shoe lace is untied";
the_insult_list[1] = "your mama!";
the_insult_list[2] = "it's hard to be insulting";
// next get the user's name
//
var the_name = prompt("What's your name?", "");
// then choose a random insult
//
var the_number = Math.random() * 5;
var insult_number = parseInt(the_number);
var the_insult = the_insult_list[insult_number];
// finally, return the personalized sass
//
alert("Hey " + the_name + " " + the_insult);
}
这种先写注释的策略不仅迫使你在写代码前思考,而且
使编码的过程看起来容易些 - 通过把任务分成小的,
易于编码的各个部分,你的问题看起来就不太象珠穆朗
玛峰,而象一群令人愉悦的起伏的小山。
最后...
总以分号结束你的每一条语句。
虽然并不是严格必需,你应该养成以分号结束每一条语
句的习惯,这样可以避免这行后面再有代码。忘了加
分号,下一行好的代码会突然产生错误。
把变量初始化为“var”,除非你有更好的理由不这样做。
用“var”把变量局域化可以减少一个函数与另一个不相
关函数相混淆的机会。
好了,既然你已经知道了如何编码,下面就让我们学习怎样使
你的JavaScript快速运行。>>
一旦你的JavaScript能运行,你就会想到使其运行得更快。
在讲解加速代码的方法之前,让我先讲讲“80/20规则”:
百分之八十的优化是由最初百分之二十的工作所完成的。竭
力实现剩余百分之二十的速度优化是一种巨大的痛苦,而且
经常导致完全不能读和难以管理的代码。简言之,如果你的
JavaScript运行得很慢,你可以用很多简单的方法来加速它,
但是除非你的代码确实运行得很慢,我不会对它进行再优化。
下面是一些使你的代码轻松运行的方法。
限制循环内的工作量
程序运行慢的最常见原因是循环内的重复工作。如果一
条命令只需要执行一次,就没有必要把它放在循环内。
例如:
var index = 0;
while (index <10)
{
var the_date = new Date();
var the_day = the_date.getDay();
var the_name = prompt("what's the kid's name? " ,"");
alert("On " + the_day + " " + the_name + " is a very special person.");
index++;
}
此程序循环执行10次。每次得到当天的日期,询问小孩
的名字,然后打印出“On Monday,so-and-so is a
very special person.”。
但是日期是不会改变的,总是今天。所以没有必要把前
两行放在循环中。把它们从循环中拿出来,让其只执行
一次而不是10次,这样会节省时间:
var index = 0;
var the_date = new Date();
var the_day = the_date.getDay();
while (index <10)
{
var the_name = prompt("what's the kid's name? " ,"");
alert("On " + the_day + " " + the_name + " is a very special person.");
index++;
}
定制if-then-else语句,按最可能到最不可能的顺序
因为if-then-else语句在遇到条件为真时结束,你可以
通过把最有可能的条件放到最开始来减少需要判断的语
句的数量。例如:
var pet = prompt("what kind of pet do you have?", "");
if (pet == "cat")
{
doCatStuff();
} else if (pet == "dog")
{
doDogStuff();
} else if (pet == "bird")
{
doBirdStuff();
} else if (pet == "lizard")
{
doLizardStuff();
}
一般来说,程序中的if子句比从lizard到dog需要执行的
逻辑判断要少。
最小化重复执行的表达式
如果你发现需要重复计算一个特定的表达式,如
var pi=22/7,只计算一次并把它放在一个全局变量中或
许是个好主意。例如,不象下面程序这样:
function theArea(radius)
{
var pi = 22/7;
var area = pi * radius * radius;
return area;
}
function theCircumference(radius)
{
var pi = 22/7;
var circumference = 2 * pi * radius;
return circumference;
}
而是这样做:
var pi = 22/7;
function theArea(radius)
{
var area = pi * radius * radius;
return area;
}
function theCircumference(radius)
{
var circumference = 2 * pi * radius;
return circumference;
}
我知道我在用一个全局变量,我也说过这不是一个好主意。
然而,一些数字,如pi,其值在程序中永远不会改变,是
此规则的特例。通过只计算pi一次,可以省去额外的计算。
或许时间上的一些小的节省,累加起来会很管用。
如果你发现代码运行很慢,你只要注意一些事情。这些都
很明显,但是当你发现你经常忽略象这样简单的优化技巧
时,你会很吃惊。
还有,我的朋友,让我们结束今天的课程,这也是整个
JavaScript高级教程的结束。如果你已经进行到这儿,
并且你至少读过过去五天课程中的一半,那么你已经看
过很多JavaScript代码了。实际上,如果你能理解跨越
第一部分和第二部分的10课的大部分内容,你就可以很
安全地把自己称为“JavaScript助手”。通往神秘真知
的路就在你的脚下