Javascript基础系列之(八)Javascript的调试与优化
Javascript的错误主要是语法错误和运行时的错误,前者在代码解析时就会出错,影响程序的运行。后者称为异常,影响它所运行的线程。下面就Javascript常见错误进行分析
1.常见的错误和异常
i.拼写错误
任何开发者在编写javascript程序时都犯过拼写错误,例如将document.getElementsByTagName()写成document.getElementByTagName(),将getElementByid()拼写成getElementByID()等。还有一些大小写的问题。例如:
If(photo.href){
var url= photo.href;
}
以上将关键字if拼写成If,导致语法错误,这种拼写错误通常可以通过编写软件高亮显示出来。
而另外一些变量上的错误拼写错误则稍微马轩写,需要开发的人耐心检查,例如:
var PhotoAlbum = "isaac";
var PhotoAlbumWife = "fresheggs";
alert("the photo:\n"+
PhotoAbulm + "and" + PhotoAlbumWife);
会提示 PhotoAbulm is not defined(变量未定义。)通过检查我们发现,定义了“PhotoAlbum”而未定义PhotoAbulm。
ii.访问未存在的变量。
最规范的变量定义都是通过var,但是javascript允许不使用关键字而直接定义变量。比如以下代码都是合法的
var isaac = "husband of fresheggs";
fresheggs = "wife of isaac";
这就无形中给检查代码增加了麻烦,像下面几行代码是比较容易发现的,浏览器也会相应的给出变量不存在的位置。
var isaac = "husband of fresheggs";
alert(fresheggs);
fresheggs = "wife of isaac";
但是很多时候,错误是十分隐蔽的
<script language="javascript"> var oInputField; var oPopDiv; var oColorsUl; var aColors = ["red", "green", "blue", "magenta", "yellow", "cornfloewrblue"]; function clearColors() { for (var i = oColorsUl.childNodes.length - 1; i >= 0; i--) oColorsUl.removeChild(oColorsUl.childNodes[i]); oPopDiv.className = "hide"; } function setColors(the_clors) { oInputField = document.forms["myForm1"].colors; oPopDiv = document.getElementById("popup"); oColorsUl = document.getElementById("colors_ul"); clearColors(); oPopDiv.className = "show"; var oLi; for (var i = 0; i < aColors.length; i++) { oLi = document.createElement("li"); oColorsUl.appendChild(oLi); oLi.appendChild(document.createTextNode(the_colors[i])); oLi.onmouseover = function() { this.className = "mouseOver"; } oLi.onmouseout = function() { this.className = "mouseOut"; } oLi.onclick = function() { oInputField.value = this.firstChild.nodeValue; clearColors(); } } } </script> <form method="post" name="myForm1"> Color: <input type="text" name="colors" id="colors" onkeyup="setColors(aColors);" /> </form> <div id="popup"> <ul id="colors_ul"></ul> </div>
浏览器告知:Uncaught ReferenceError: the_colors is not defined the_colors变量未定义,仔细检查32行
oLi.appendChild(document.createTextNode(the_colors[i]));
确没有错误,其实错误在第22行拼写错误。这样提示和错误不符大大增加了检查的难度。
function setColors(the_clors)
iii.括号不匹配
编写较长的逻辑关系代码时,常常需要反复的使用花括弧和小括弧,很多时候因为删除某些代码导致括号和括号后的个数不匹配。
function testPic(x, start, end) { if (x <= end && x >= start) { if (x == start) { alert(x + " is the start of the range"); } if (x == end) { alert(x + " is the end of the range"); } if (x != start && x != end) { alert(x + " is in the range"); } else { alert(x + " is not in the range"); } } testPic(7, 5, 8);
错误报告10行缺少},但行数不一定在10行。
正确代码
function testPic(x, start, end) { if (x <= end && x >= start) { if (x == start) { alert(x + " is the start of the range"); } if (x == end) { alert(x + " is the end of the range"); } if (x != start && x != end) { alert(x + " is in the range"); } } else { alert(x + " is not in the range"); } } testPic(7, 5, 8);
从正确的代码可以看到,要避免括号遗漏的最有效办法是养成规范的编码习惯,适当应用tab 空行等
iiii.字符串和变量连接错误
javascript在输出结果时常常需要将字符串和变量连接,因此加号和引号都很多,容易错漏
father = "isaac";
mother = "fresheggs";
child = "none";
family = father+" "+mother+" "child;
如上面的代码,在child前边漏掉了+号,导致程序错误
还有一种典型的连接错误就是javascript的类型转化。如下代码
a = 10;
b = 20;
c = 30;
alert("a+b+c="+a+b+c);
执行结果102030显然不是我们所想要的结果,解决上述问题的办法就是利用括号把数值部分和文字串部门分别做处理,然后加起来。
a = 10;
b = 20;
c = 30;
alert("a+b+c="+(a+b+c));
iiiii.等号与赋值混淆。
在条件判断中,常常会把等号==误写成赋值符号= ,这样的语法没有任何问题,javascript会把它处理成赋值是否成功来判断真假
if (isaac = "talking"){
fresheggs.hear();
}
以上代码的执行结果是将变量isaac赋值为"talking",如果赋值成功,则执行花括弧中的fresheggs,但开发者的本意是
if (isaac == "talking"){
fresheggs.hear();
}
即当isaac的变量值为"talking"时,执行fresheggs.hear();这样的问题在程序检查中是很难发现的
2.错误处理
i.用alert()和document.write()来监视变量值
在日常开发中,alert()和document.write()来监视变量值,恐怕是最常用和有些的解决的方法。
alert()在开发中会跳出目前的变量值,并中止程序的运行,直到点击确定后继续执行。document.write()方法则是输出变量值后继续执行程序,因为两者的执行方法不同,所以给开发者带来了选择上的技巧。
for(var i=0;i<aColors.length;i++)
if(aColors[i].indexOf(oInputField.value)==0)
aResult.push(aColors[i]);
为了检测传入aResult的值,常给if加入alert()方法来检测,如果值很多,最好使用document.write()方法来检测传入值,避免反复的进行确定
ii.使用onerro方法
当页面出现错误时,onerro事件使用window对象触发,告诉开发者哪出了错误。
<script language="javascript"> window.onerror = function(){ alert("出错啦!"); } </script> </head> <body onload="nonExistent()"> </body>
利用window.onload引用一个不存在的函数引发异常,浏览器本身也出现了调试错误,要屏蔽错误信息,只需要在onerror函数后面加return true即可。
<script type="text/javascript"> window.onerror = function(){ alert("出错啦!"); return true; //屏蔽系统事件 } </script> </head> <body onload="nonExistent()"> </body>
但是这样对处理错误没有任何帮助,其实onerror提供了三个参数确定错误性质
<script type="text/javascript"> window.onerror = function(sMessage,sUrl,sLine){ alert("出错了:\n"+sMessage+"\n 地址:"+ sUrl +"\n行号:"+sLine); return true; //屏蔽系统事件 } </script>
iii.使用try...catch语句找到错误
js中借鉴了java的try...catch方法,该语句会先运行try里边的方法,如果出现错误则跳转运行catch()里面的代码,如果有finnally,则无论是否错误都运行其后的代码
try...catch的语法如下:
try...catch的代码语法如下:
try{
//代码
}catch([exception]){
//如果try里面的代码有错,则运行
}[finally{
//最后运行,无论是否出错
}]
例子:使用try...catch语法找错误
<script type="text/javascript"> try{ alert("this is an example"); alert(fresheggs); } catch(exception){ var sError = ""; for(var i in exception) sError += i + ":" + exception[i] + "\n"; alert(sError); } </script>
在以上语法中特意输出一个未定义的变量fresheggs,导致了异常,由此运行catch里面的代码,此时浏览器会跳出错误
通过try...catch能轻松的找到错误,但可惜的是该语句并不能很好的处理语法错误。
如下:try语句中出现了语法错误,而这个代码并没有运行catch中的代码。
<script type="text/javascript"> try{ alert("this is an example") ); } catch(exception){ var sError = ""; for(var i in exception) sError += i + ":" + exception[i] + "\n"; alert(sError); } </script>
3.使用调试器
尽管javascript不具有调试功能,但是我们借助ie或者firefox浏览器的控制台或者调试插件能起到调试作用。
i.使用firefox错误控制台进行调试
使用firefox错误控制台,我们在页面中右键鼠标,审查元素,调到控制台,js选项就可以看到浏览器把页面运行的所有js错误信息都传到错误控制台上,单击每条信息就会打开对于的代码。
firefox错误调试平台比ie准确的多,在没有特殊需求的调试要求下,是个不错的选择。
ii.使用Microsoft Script debugger进行调试
http://www.microsoft.com/zh-cn/download/details.aspx?id=23992
iii.使用Venkman
在火狐浏览器中直接安装即可。
关于vankman 请参看http://jiangzhengjun.iteye.com/blog/481500一文。
4.javascript的优化
i.减缓代码的下载时间。
web浏览器下载的是javascript的源码,其中包含的长变量名、注释、空格换行等内容减缓了代码的下载时间。这些字符对于团队编写代码十分有效但在工程上传到服务器时应当删除。
如以下代码
<script type="text/javascript"> function showMeTheMoney(money){ if(!money){ return false; }else{ ... } } </script>
可以优化成
function showMeTheMoney(money){if(!money){return false;}else{...}}
这样优化后节省了20个字符,若这个项目是一个很大的工程,将节省很大的服务器空间,不但提高了下载速度,也减少了服务器的压力。另外对于布尔型的值,true和false,true都可以用1来替换,false可以用0来替换,对于true来说节省了3个字符,对于false来说节省了4个字符,例如
var bSearch = false; for(var i = 0;i<aChoices.length&&!bSearch;i++){ if(aChoices[i]==vValue) bSearch=true; }
替换成
var bSearch = 0; for(var i = 0;i<aChoices.length&&!bSearch;i++){ if(aChoices[i]==vValue) bSearch=1; }
代码的执行效率,结果都相同,但节省了7个字符。
代码中常常会出现检测某个值是否为有效值的语句,而很多条件非的判断就是判断某个变量是否为"undefined"、"null"、"false"
if(myValue !=undefined){
...//
}
if(myValue !=null){
...//
}
if(myValue !=false){
..///
}
尽管这些操作符都正确,但使用“逻辑非”操作符“!”也能达到同样的效果。
if(!myValue){
...//
}
而且不影响代码的可读性。类似的替换还有
var myTalk = new Array myTalk();
var myTalk =[];
var myTack = new Array myTack{};
var myTack ={};
另外,在编写代码时为了提高可读性,函数名称,变量名尽量使用简单明了的代码
function AddThreeVarsRogether(first Var,second Var,Third Var){
return(firstVar+secondVar+thirdVar)
}
可以缩写成
function A(a,b,c){return(a+b+c)}
ii.合理声明变量
合理的声明全局变量和局部变量,一个原则,尽量使用局部变量,即用即销。
iii.使用内置函数缩短编译时间
因为尽量的使用内置函数,因为这些函数类似C,Java都是通过编译好的,比起实时编译的javascript效率高很多。
function myPower(iNum, n){ var iResult = iNum; for(var i=1;i<n;i++) iResult *= iNum; return iResult; } document.write(myPower(5,3));
例如计算指数,以上的代码逻辑没有问题,但是比内置的函数Math.pow(5,3);执行起来效率低很多
如以下代码
function myPower(iNum, n){ var iResult = iNum; for(var i=1;i<n;i++) iResult *= iNum; return iResult; } var myDate1 = new Date(); for(var i=0;i<150000;i++){ myPower(7,8); //自定义方法 } var myDate2 = new Date(); document.write(myDate2-myDate1); document.write("<br>"); myDate1 = new Date(); for(var i=0;i<150000;i++){ Math.pow(7,8); //采用系统内置方法 } myDate2 = new Date(); document.write(myDate2-myDate1);
让指数函数各运算15万次,然后分别计算运行时间,发现内置方法要比myPower方法快很多。
iiii.合理书写if语句。
if可能是代码中使用频率最多的,然而可惜他的执行效率不高,因此,在多个if else时,尽量把可能性最高的放在第一个,把可能性第二高的放在第二个,以此类推。
var iNum = 1111; if(iNum>0&&iNum<100){ alert("在0-100之间!") }else if(iNum>100&&iNum<200){ alert("在100-200之间") }else if(iNum>200&&iNum<300){ alert("200-300之间") }else {alert("小于0或大于300!")}
另外,以上代码还可以优化成
if (iNum > 0) { if (iNum < 100) { alert("在0-100之间"); } else { if (iNum < 200) { alert("在100-200之间!"); } else { if (iNum < 300) { alert("200-300之间!"); } else { alert("大于300!"); } } } }else{ alert("小于0!") }
以上代码看上去比较复杂,但考虑了多个因素,执行起来要比原来的代码效率高。
另外 ,在超过两种情况下,最好使用switch语句,经常使用switch代替if语句,可使代码效率执行高10倍。由于case可以适合多种类型,大大提高了执行效率。
iiiii.最小化语句数量
var myNumber = 365;
var myString = "yellows";
var aMyNum = [8,8,4,5,6];
var oData = new Date();
以上代码可以用var一次性定义
var myNumber = 365,myString = "yellows",aMyNum = [8,8,4,5,6],oData = new Date();
同样在很多迭代运算时,也尽可能少的减少代码,代码中的语句越少
例如:
var sCar = aCars[i];
i++;
可以写成
var sCar = aCars[i++]
iiiiii.合理使用DOM
javascript对dom的操作可能是最耗时的操作之一,每次javascript对dom操作都要从新渲染页面一次。有比较明显的时间消耗,尽可能的方法就是不在页面上进行dom操作。
<ul id="parentUl"> <li> 原li </li> </ul> <script type="text/javascript"> function addElementLi(obj) { var ul = document.getElementById(obj); for (var i = 0; i < 10; i++) { var li = document.createElement("li"); li.setAttribute("id", "newli" + i); li.innerHTML = "动态添加li"; ul.appendChild(li); } } addElementLi("parentUl"); </script>
(本文结束,欢迎大家点评)