条件语句(3个陷阱) 所有的条件语句都必须位于圆括号中。执行语句主体不管是一句还是多句都强烈建议用花括号包围起来,这样能避免很多因修改或嵌套而产生的潜在错误。 if(var1 == var2){//statement}不要犯无意地使用赋值运算符的错误:把第二个参数的值赋给第一个参数。因为它是一个逻辑问题,它将一直返回true且不会报错。 if(var1 = var2){} // 返回true。把var2赋值给var1JavaScript是弱类型,除了在switch语句中。当JavaScript在case比较时,它是非弱类型。 var myVar = 5; if(myVar == '5'){ //返回true,因为JavaScript是弱类型 alert("hi"); //这个alert将执行,因为JavaScript通常不在意数据类型 } switch(myVar){ case '5': alert("hi"); //这个alert将不会执行,因为数据类型不匹配 } 换行 当心JavaScript中的硬换行。换行被解释为表示行结束的分号。即使在字符串中,如果在引号中包括了一个硬换行,那么你会得到一个解析错误(未结束的字符串)。 var bad = '<ul id="myId"> <li>some text</li> <li>more text</li> </ul>'; // 未结束的字符串错误 var good = '<ul id="myId">' + ‘<li>some text</li>‘ + ‘<li>more text</li>‘ + ‘</ul>’; // 正确 前面讨论过的换行被解释为分号的规则并不适用于控制结构这种情况:条件语句关闭圆括号后的换行并不是给其一个分号。 一直使用分号和圆括号,那么你不会因换行而出错,你的代码易于阅读,且除了那些不使用分号的怪异源码外你会少一些顾虑:所以当移动代码且最终导致两个语句在一行时,你无需担心第一个语句是否正确结束。 多余的逗号 变量作用域 JavaScript中的许多问题都来自于变量作用域:要么认为局部变量是全局的,要么用函数中的局部变量覆盖了全局变量。为了避免这些问题,最佳方案是根本没有任何全局变量。但是,如果你有一堆,那么你应该知道这些陷阱。 不用var关键字声明的变量是全局的。记住使用var关键字声明变量,防止变量具有全局作用域。在下面例子中,在函数中声明的变量具有全局变量,因为没有使用var关键字声明: anonymousFuntion1 = function(){ globalvar = 'global scope'; //全局声明,因为“var”遗漏了 return localvar; }(); alert(globalvar); //弹出“global scope”,因为函数中的变量是全局声明 anonymousFuntion2 = function(){ var localvar = 'local scope'; //使用“var”局部声明 return localvar; }(); alert(localvar); //错误 “localvar未定义”。没有全局定义localvar 作为参数引进到函数的变量名是局部的。如果参数名也是一个全局变量的名字,像参数变量一样有局部作用域,这没有冲突。如果你想在函数中改变一个全局变量,这个函数有一个参数复制于这个全局变量名,记住所有全局变脸都是window对象的属性。 var myscope = "global"; function showScope(myscope){ return myscope; //局部作用域,即使有一个相同名字的全局变量 } alert(showScope('local')); function globalScope(myscope){ myscope = window.myscope; //全局作用域 return myscope; } alert(globalScope(’local’)); 你甚至可以在循环中声明变量: for(var i = 0; i < myarray.length; i++){}覆盖函数/重载函数 当你不止一次的声明一个函数时,这个函数的最后一次声明将覆盖掉该函数的所有前面版本且不会抛出任何错误或警告。这不同于其他的编程语言,像Java,你能用相同的名字有多重函数,只要它们有不同的参数:调用函数重载。在JavaScript中没有重载。这使得不能在代码中使用JavaScript核心部分的名字极其重要。也要当心包含的多个JavaScript文件,像一个包含的脚本文件可能覆盖另一个脚本文件中的函数。请使用匿名函数和名字空间。 (function(){ // creation of my namespace 创建我的名字空间 if(!window.MYNAMESPACE) { window['MYNAMESPACE'] = {}; } //如果名字空间不存在,就创建它 //这个函数仅能在匿名函数中访问 function myFunction(var1, var2){ //内部的函数代码在这儿 } // 把内部函数连接到名字空间上,使它通过使用名字空间能访问匿名函数的外面 window['MYNAMESPACE']['myFunction'] = myFunction; })(); // 圆括号 = 立即执行 // 包含所有代码的圆括号使函数匿名 这个例子正式为了实现解决上一个陷阱“变量作用域”的最佳方案。匿名函数详细内容请看《Javascript的匿名函数》。YUI整个库只有YAHOO和YAHOO_config两个全局变量,它正是大量应用匿名函数和命名空间的方法来实现,具体请看《Javascript的一种模块模式》。 字符串替换 一个常见错误是假设字符串替换方法的行为会对所有可能匹配都产生影响。实际上,JavaScript字符串替换只改变了第一次发生的地方。为了替换所有发生的地方,你需要设置全局标识。同时需要记住String.replace()的第一个参数是一个正则表达式。 var myString = "this is my string"; myString = myString.replace("","%20"); // "this%20is my string" myString = myString.replace(/ /,"%20"); // "this%20is my string" myString = myString.replace(/ /g,"%20"); // "this%20is%20my%20string" parseInt 在JavaScript得到整数的最常见错误是假设parseInt返回的整数是基于10进制的。别忘记第二个参数基数,它能是从2到36之间的任何值。为了确保你不会弄错,请一直包含第二个参数。 parseInt('09/10/08'); //0 parseInt(’09/10/08′,10); //9, 它最可能是你想从一个日期中得到的值 如果parseInt没有提供第二个参数,则前缀为 ‘0x’ 的字符串被当作十六进制,前缀为 ‘0′ 的字符串被当作八进制。所有其它字符串都被当作是十进制的。如果 numString 的前缀不能解释为整数,则返回 NaN(而不是数字)。 ‘this’ 另一个常见的错误是忘记使用“this”。在JavaScript对象中定义的函数访问这个对象的属性,但没有使用引用标识符“this”。例如,下面是错误的: function myFunction() { var myObject = { objProperty: "some text", objMethod: function() { alert(objProperty); } }; myObject.objMethod(); } function myFunction() { var myObject = { objProperty: "some text", objMethod: function() { alert(this.objProperty); } }; myObject.objMethod(); } 有一篇A List Apart文章用通俗易懂的英文表达了this绑定的问题。 对this使用最大的陷阱是this在使用过程中其引用会发生改变: <input type="button" value="Gotcha!" id="MyButton"> <script> var MyObject = function () { this.alertMessage = "Javascript rules"; this.ClickHandler = function() { alert(this.alertMessage ); //返回结果不是”JavaScript rules”,执行MyObject.ClickHandler时, //this的引用实际上指向的是document.getElementById("theText")的引用 } }(); document.getElementById(”theText”).onclick = MyObject.ClickHandler </script> 其解决方案是: var MyObject = function () { var self = this; this.alertMessage = “Javascript rules”; this.OnClick = function() { alert(self.value); } }(); 类似问题的更多细节和解决方案请看《JavaScript作用域的问题》。 switch语句Estelle Weyl写了一篇switch statement quirks,其要点是:没有数据类型转换 一个匹配,所有的表达式都将执行直到后面的break或return语句执行 你可以对一个单独语句块使用多个case从句。 undefined ≠ nullnull是一个对象,undefined是一个属性、方法或变量。存在null是因为对象被定义。如果对象没有被定义,而测试它是否是null,但因为没有被定义,它无法测试到,而且会抛出错误。if(myObject !== null && typeof(myObject) !== 'undefined') { //如果myObject是undefined,它不能测试是否为null,而且还会抛出错误 } if(typeof(myObject) !== 'undefined' && myObject !== null) { //处理myObject的代码 } 事件处理陷阱刚接触事件处理时最常见的写法就是类似:window.onclick = MyOnClickMethod这种做法不仅非常容易出现后面的window.onclick事件覆盖掉前面的事件,还可能导致大名顶顶的IE内存泄露问题。为了解决类似问题,4年前Simon Willison就写出了很流行的addLoadEvent():function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; }else { window.onload = function() { oldonload(); unc(); } } } addEvent(window,'load',func1,false); addEvent(window,'load',func2,false); addEvent(window,'load',func3,false); 当然在JavaScript库盛行的现在,使用封装好的事件处理机制是一个很好的选择,比如在YUI中就可以这样写:YAHOO.util.Event.addListener(window, "click", MyOnClickMethod);