我们使用习惯用法可以使我们的意图更加的清晰和简洁。
使用==时,当心强制转换
考虑下面函数:
1function gw(f){ 2 if (d.w.sv.checked == true) { 3 zv = 'on'; 4 } 5 else { 6 zv = 'off'; 7 } 8 procframe.location.replace("http://b.www.yahoo.com/module/wtr_tr.php?p=" + 9 escape(f.p.value) + 10 "&sv=" + 11 zv); 12 return false; 13}
==运算符不应该被用着和true比较值,因为它要执行强制转换。如果我们想确定d.w.sv.checked是否是布尔值
true,我们必须用===运算符。如果我们仅在意一个值是真实存在的不是假的,最好不要用相等运算符。
例如,由于强制转换:1 == true是真,1 === true是假。==运算符隐藏了类型错误。
使用?:运算符选择两值之一
if语句通常被用来从两个值中选择一个。这应该是三元操作符?:最适合的。
zv = d.w.sv.checked ? 'on' : 'off';
绝不使用隐含的全局变量
变量zv不是作为一个var或函数参数来声明的,所以它是一个隐式的全局变量。如果这个页面的另一个函数使用了同样名字的全局变量,则可能得到一个 失败的结果。这样的臭虫(bug)是非常难以发现,却很容易避免。这个例子中,我们既可以声明zv为一个var,也可以发现它仅仅被使用过一次而整个去掉 它。
1function gw(f){ 2 procframe.location.replace("http://b.www.yahoo.com/module/wtr_tr.php?p=" + 3 escape(f.p.value) + 4 "&sv=" + 5 d.w.sv.checked ? 'on' : 'off'); 6 return false; 7}
绝不使用?:运算符选择两种行为之一
我们常质疑那些返回一个常量的函数,但这有时是在浏览器环境下所必需的。
下面我们看一个不正确使用?:运算符的例子。它常被用于在两个任务间选择。
1function u(o, z){ 2 var em = o.id.substring(1); 3 var p = d.getElementById('e' + em); 4 if (p) { 5 (z == 0) ? p.style.backgroundColor = '#fff' : p.style.backgroundColor = '#989898'; 6 } 7 p = d.getElementById('e' + (em - 1)); 8 if (p) { 9 (z == 0) ? p.style.backgroundColor = '#fff' : p.style.backgroundColor = '#989898'; 10 } 11}
对Z的判断是模糊不清的。Z正好等于0时我们选择#fff颜色,那么,Z不等于时?如上所述似乎指明的是前者,但它实际上是后者。在这个例子中幸运的是,我们大概想要的就是后者,所以它不是技术上错误(这次)。但是在文体上只糟糕的。
我们可以用if代替?:,但碰巧的是这些值对应的是同一个左值(lvalue),所以我们无需if就可以改正这个错误。
1function u(o, z){ 2 var em = o.id.substring(1), p = d.getElementById('e' + em); 3 if (p) { 4 p.style.backgroundColor = z ? '#fff' : '#989898'; 5 } 6 p = d.getElementById('e' + (em - 1)); 7 if (p) { 8 p.style.backgroundColor = z ? '#fff' : '#989898'; 9 } 10}
使用||运算符指定一个默认值
事件处理程序依赖于浏览器。理想情况下,应用程序应该通过公共库隔绝对浏览器的依赖。当没有这样的库时,就会有些函数发生如下情况:
1function md(e){ 2 (window.event) ? ev = window.event : ev = e; 3 (ev.target) ? sr = ev.target : sr = ev.srcElement; 4 if (ev && sr && sr.id == "fp" || sr.id == "sb") 5 st = 1; 6 if (sr.className.indexOf("pllist") < 0 && sr.className != "more" && 7 sr.className != "plinkc" && 8 sr.tagName != "scrollbar " && 9 _toClose && 10 _toCloseNorgie) { 11 d.getElementById(_toClose).innerHTML = ""; 12 _toClose = ""; 13 _toCloseNorgie.parentNode.className = ''; 14 _toCloseNorgie = ''; 15 } 16}
一些浏览器把事件对象作为一个参数传给事件管理程序。微软选用把事件对象放入到一个全局的事件变量中。在Javascript中,全局变量是全局对 象的成员。在浏览器中,全局对象始终包含一个window对象成员,其值是全局对象。当测试一个变量是否存在时,通过window访问全局变量是避免未定 义变量错误的一种方法。无论如何,做这样的测试不应该是必要的。
我们能通过问它是否是另外一种,来代替首先判断是否是微软事件。
ev = e || event;我们用||(默认)运算符。如果e是真,我们将有它的值,但是如果e是假,则我们将用event。
在下一个语句,我们又用||运算符去确定sr是哪个值。
我们应该用var去声明ev和sr来避免全局冲突:
1function md(e){ 2 var ev = e || event, sr = ev.target || ev.srcElement; 3 if (sr && (sr.id == 'fp' || sr.id == 'sb')) { 4 st = 1; 5 } 6 if (sr.className.indexOf('pllist') < 0 && sr.className != 'more' && 7 sr.className != 'plinkc' && 8 sr.tagName != 'scrollbar ' && 9 _toClose && 10 _toCloseNorgie) { 11 d.getElementById(_toClose).innerHTML = ''; 12 _toClose = ''; 13 _toCloseNorgie.parentNode.className = ''; 14 _toCloseNorgie = ''; 15 } 16}
全局变量是魔鬼
下面我们看到另一个时间处理程序。正如你所料,它重复像前面一样破坏风格。
1function kd(e){ 2 (window.event) ? ev = window.event : ev = e; 3 (ev.target) ? el = ev.target : el = ev.srcElement; 4 if (ev && el) { 5 code = ev.keyCode; 6 id = el.id; 7 } 8 else { 9 return; 10 } 11 ctn = lt.id.substring(1); 12 if (code == 13) { 13 return; 14 } 15 else 16 if ((code == 191 || code == 222) && id != 'fp') { 17 _ffs = 1; 18 gk = 0; 19 } 20 else 21 if ((code < 31 || code > 41) && 22 (code < 16 || code > 18) && 23 code != 9 && 24 code != 8) { 25 gk = 1; 26 } 27 else { 28 gk = 0; 29 } 30 if (!_ffs && (id == 'fp' || id == 'st')) { 31 if (code == 9) { 32 if (box.value == '' || (box.value != '' && (at == 1 || ev.shiftKey))) { 33 mt(ctn); 34 } 35 else 36 if (id == 'st' && box.value != '' && at == 0) { 37 at = 1; 38 mt(ctn); 39 } 40 } 41 else 42 if (id == 'fp' && gk == 0 && (box.value == '' && st == 0) && 43 !ev.shiftKey && 44 !ev.ctrlKey && 45 !ev.altKey) { 46 d.getElementById('mk').focus(); 47 d.getElementById('mk').blur(); 48 } 49 else 50 if (gk == 1) { 51 at = 0; 52 } 53 } 54 else 55 if ((id == 'mk2' && box.value != '' && ev.shiftKey && code == 9) || 56 (id == 'm6' && !ev.shiftKey && code == 9)) { 57 d.getElementById('mk').focus(); 58 } 59 else 60 if (!_ffs && gk == 1 && el.type != 'text' && !ev.ctrlKey && !ev.altKey) { 61 box.value = ''; 62 box.focus(); 63 } 64} 65function mt(ctn){ 66 if ((ev && !ev.ctrlKey && !ev.altKey) || !ev) { 67 if (ev.shiftKey) { 68 nextTab = parseInt(ctn) - 1; 69 } 70 else { 71 nextTab = parseInt(ctn) + 1; 72 } 73 if (nextTab == 0) { 74 d.getElementById('mk').focus(); 75 } 76 else 77 if (nextTab < 8) { 78 t(d.getElementById('v' + nextTab)); 79 } 80 else { 81 return; 82 } 83 } 84}
有意思的是它有一个同伴函数mt,它仅被kd调用。mt被传给一个参数ctn,但kd和mt之间的通讯大部分是通过全局变量。
使用内部函数避免全局变量
我们可以通过增加传递给mt的参数数量来除掉所有的全局变量。但代替方案,我们将使mt变成kd的内部函数。作为一个内部函数,mt能访问kd的所有变量。
1function kd(e){ 2 var ev = e || event, el = ev.target || ev.srcElement, cnt, code = ev.keyCode, gk, id = el.id, ctn = lt.id.substring(1); 3 4 function mt(){ 5 var nextTab; 6 if (!ev.ctrlKey && !ev.altKey) { 7 nextTab = parseInt(ctn) + ev.shiftKey ? -1 : 1; 8 if (!nextTab) { 9 d.getElementById('mk').focus(); 10 } 11 else 12 if (nextTab < 8) { 13 t(d.getElementById('v' + nextTab)); 14 } 15 } 16 } 17 18 if (code == 13) { 19 return; 20 } 21 else 22 if ((code == 191 || code == 222) && id != 'fp') { 23 _ffs = 1; 24 gk = 0; 25 } 26 else 27 if ((code < 31 || code > 41) && 28 (code < 16 || code > 18) && 29 code != 9 && 30 code != 8) { 31 gk = 1; 32 } 33 else { 34 gk = 0; 35 } 36 if (!_ffs && (id == 'fp' || id == 'st')) { 37 if (code == 9) { 38 if (box.value == '' || 39 (box.value != '' && (at == 1 || ev.shiftKey))) { 40 mt(); 41 } 42 else 43 if (id == 'st' && box.value != '' && at == 0) { 44 at = 1; 45 mt(); 46 } 47 } 48 else 49 if (id == 'fp' && gk == 0 && (box.value == '' && st == 0) && 50 !ev.shiftKey && 51 !ev.ctrlKey && 52 !ev.altKey) { 53 d.getElementById('mk').focus(); 54 d.getElementById('mk').blur(); 55 } 56 else 57 if (gk == 1) { 58 at = 0; 59 } 60 } 61 else 62 if ((id == 'mk2' && box.value != '' && ev.shiftKey && code == 9) || 63 (id == 'm6' && !ev.shiftKey && code == 9)) { 64 d.getElementById('mk').focus(); 65 } 66 else 67 if (!_ffs && gk == 1 && el.type != 'text' && !ev.ctrlKey && 68 !ev.altKey) { 69 box.value = ''; 70 box.focus(); 71 } 72}
在函数kd中,从两个地方调用函数mt。通过使它成为一个内部函数,我们能有效的减少kd所用到的全局变量的数目,这将降低了干扰其他组件的可能性。kd依旧是一个烂摊子,但它现在不是一无是处的烂摊子。
作者:Douglas Crockford
原文:The Elements of JavaScript Style Part Two
翻译:秦歌(随网之舞 )