深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点

来源:互联网 发布:疲劳驾驶数据 编辑:程序博客网 时间:2024/05/19 20:23

预解析:var散布的问题(Hoisting: A Problem with Scattered vars)

JavaScript中,你可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为 hoisting(悬置/置顶解析/预解析)。当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。对于JavaScript,只 要你的变量是在同一个作用域中(同一函数),它都被当做是声明的,即使是它在var声明前使用的时候。看下面这个例子:

// 反例myname = "global"; // 全局变量function func() {    alert(myname); // "undefined"    var myname = "local";    alert(myname); // "local"}func();

在这个例子中,你可能会以为第一个alert弹出的是”global”,第二个弹出”loacl”。这种期许是可以理解的,因为在第一个alert 的时候,myname未声明,此时函数肯定很自然而然地看全局变量myname,但是,实际上并不是这么工作的。第一个alert会弹 出”undefined”是因为myname被当做了函数的局部变量(尽管是之后声明的),所有的变量声明当被悬置到函数的顶部了。因此,为了避免这种混 乱,最好是预先声明你想使用的全部变量。

上面的代码片段执行的行为可能就像下面这样:

myname = "global"; // global variablefunction func() {   var myname; // 等同于 -> var myname = undefined;   alert(myname); // "undefined"   myname = "local";   alert(myname); // "local"}func();

for循环(for Loops)

在for循环中,你可以循环取得数组或是数组类似对象的值,譬如arguments和HTMLCollection对象。通常的循环形式如下:

// 次佳的循环for (var i = 0; i < myarray.length; i++) {   // 使用myarray[i]做点什么}

这种形式的循环的不足在于每次循环的时候数组的长度都要去获取下。这意味着每次你访问任何集合的长度,你要实时查询DOM,而DOM操作一般都是比较昂贵的。

这就是为什么当你循环获取值时,缓存数组(或集合)的长度是比较好的形式,正如下面代码显示的:

for (var i = 0, max = myarray.length; i < max; i++) {   // 使用myarray[i]做点什么}

这样,在这个循环过程中,你只检索了一次长度值。

最后一个需要对循环进行调整的是使用下面表达式之一来替换i++。

i = i + 1i += 1

JSLint提示您这样做,原因是++和–-促进了“过分棘手(excessive trickiness)”。//zxx:这里比较难翻译,我想本意应该是让代码变得更加的棘手
如果你直接无视它,JSLint的plusplus选项会是false(默认是default)。

还有两种变化的形式,其又有了些微改进,因为:

  • 少了一个变量(无max)
  • 向下数到0,通常更快,因为和0做比较要比和数组长度或是其他不是0的东西作比较更有效率
//第一种变化的形式:var i, myarray = [];for (i = myarray.length; i–-;) {   // 使用myarray[i]做点什么}//第二种使用while循环:var myarray = [],    i = myarray.length;while (i–-) {   // 使用myarray[i]做点什么}

这些小的改进只体现在性能上,此外JSLint会对使用i–-加以抱怨。

switch模式(switch Pattern)

你可以通过类似下面形式的switch语句增强可读性和健壮性:

var inspect_me = 0,    result = '';switch (inspect_me) {case 0:   result = "zero";   break;case 1:   result = "one";   break;default:   result = "unknown";}

这个简单的例子中所遵循的风格约定如下:

  • 每个case和switch对齐(花括号缩进规则除外)
  • 每个case中代码缩进
  • 每个case以break清除结束
  • 避免贯穿(故意忽略break)。如果你非常确信贯穿是最好的方法,务必记录此情况,因为对于有些阅读人而言,它们可能看起来是错误的。
  • 以default结束switch:确保总有健全的结果,即使无情况匹配。

parseInt()下的数值转换(Number Conversions with parseInt())

使用parseInt()你可以从字符串中获取数值,该方法接受另一个基数参数,这经常省略,但不应该。当字符串以”0″开头的时候就有可能会出问 题,例如,部分时间进入表单域,在ECMAScript 3中,开头为”0″的字符串被当做8进制处理了,但这已在ECMAScript 5中改变了。为了避免矛盾和意外的结果,总是指定基数参数。

var month = "06",    year = "09";month = parseInt(month, 10);year = parseInt(year, 10);

此例中,如果你忽略了基数参数,如parseInt(year),返回的值将是0,因为“09”被当做8进制(好比执行 parseInt( year, 8 )),而09在8进制中不是个有效数字。

左花括号的位置(Opening Brace Location)

开发人员对于左大括号的位置有着不同的偏好——在同一行或是下一行。

if (true) {   alert("It's TRUE!");}//或if (true){   alert("It's TRUE!");}

这个实例中,仁者见仁智者见智,但也有个案,括号位置不同会有不同的行为表现。这是因为分号插入机制(semicolon insertion mechanism)——JavaScript是不挑剔的,当你选择不使用分号结束一行代码时JavaScript会自己帮你补上。这种行为可能会导致麻 烦,如当你返回对象字面量,而左括号却在下一行的时候:

// 警告: 意外的返回值function func() {   return  // 下面代码不执行   {      name : "Batman"   }

如果你希望函数返回一个含有name属性的对象,你会惊讶。由于隐含分号,函数返回undefined。前面的代码等价于:

// 警告: 意外的返回值function func() {   return undefined;  // 下面代码不执行   {      name : "Batman"   }}

总之,总是使用花括号,并始终把在与之前的语句放在同一行:

function func() {   return {      name : "Batman"   };}
阅读全文
0 0
原创粉丝点击