[译]JavaScript中几种愚蠢的写法
原文:http://james.padolsey.com/javascript/js-adolescence
对于我来说,有一段时间可以描述成为是我在编程生涯(更具体点讲是指JavaScript)中的青少年时期.这一时期的特点是懒惰而又自大.我只认为我自己是对的,其他人都是错的.
今天,我给出一些我以前认为是对的,但现在发现是很愚蠢(在大多数情况下)的JavaScript写法:
1.使用逻辑运算符作为语句
a() || b.foo.bar(); m && c() ? d() : l || m.foo[bee](3);
为了显示自己的技能有多高超而放弃常规的写法并不会得到什么真正的好处.
译者注:这里我得解释一下.作者的意思是,包含逻辑运算符的表达式只应该作为某个语句的一部分,而不应该作为单独的表达式语句,主要指的是&&和||.下面详细讲一下.
新手可能不知道JavaScript中逻辑或||和逻辑与&&的特点,他们只会使用这两个运算符最基本的语法:
if ((x < 100 || x > -100) && x !== 0) {
alert("x的绝对值小于100,且x不等于0")
}但其实,JavaScript中的逻辑或和逻辑与运算符返回的并不一定是布尔值,确切的说法是:返回的是两个操作数其中的某一个.由于这个特性,就有了另外一种很常见的用法(下面的例子来自《JavaScript语言精粹》):
逻辑或||运算符可以用来设置默认值:
var middle = stooge["middle-name"] || "(none)";
var status = flight.status || "unknown";尝试从值为undefined的变量上读取属性值会抛出TypeError异常,可以使用逻辑与&&操作符来防范这种情况:
flight.equipment // undefined
flight.equipment.model // throw "TypeError"
var model = flight.equipment && flight.equipment.model // undefined上面的两种用法都把包含逻辑运算符的表达式作为了某个语句的一部分,也就是说:都用到了这些表达式的返回值.下面的使用场景就不一样了:
typeof x == "object" && x !== null && alert("x是一个对象")这个例子中,整个表达式的值完全没有被用到,表达式作为了一个单独的语句,目的就是为了有条件的执行最右边的函数.如果这么写的话,你应该使用更常规的写法,以提高可读性和可维护性:
if (typeof x == "object" && x !== null) {
alert("x是一个对象")
}
2.总在作用域的最上方定义变量
function calculate() {
var a, m, t, y, This, is, really, understandable = false;
// 其他代码
}
我现在的做法是:在最合适的地方声明不同的变量.通常情况下,也就是作用域的最上方,但也有例外,这时,不应该被那些所谓的代码风格限制住.
译者注:网上有不少知名人士或者知名公司的代码风格指南,这些东西值的参考,但并不是说必须遵循,因为风格之所以为风格,就是因为这东西是很主观的,很难说谁对谁错.
下面列举几个比较知名的JavaScript代码风格指南:
就变量声明,下面引用一下"尼古拉斯·泽卡斯"的《Maintainable JavaScript》一书中的一小段:
我个人的喜好是将所有的var语句都合并,并且每个变量声明都放在单独的一行内.等号也要对齐.那些没有初始化的变量,应该放在该var语句靠后面的部分,如下:
function doSomethingWithItems(items) {
var value = 10,
result = value + 10,
i,
len;
for (i=0, len=items.length; i < len; i++) {
doSomething(items[i]);
}
}
3.反复使用行内对象字面量
function prepareRequest(mode, updateId, value) {
var req = {
mode: mode,
updateId: updateId,
value: value,
uid: genUID()
};
// 其他代码.
}
如果你需要重复的定义一些结构相同的对象字面量,那么创建一个分离的"类"能够让你的代码更清晰,性能更好.例如,上面的代码可以重构成一个Request"类".
译者注:作者的意思应该是使用构造函数生成对象.
function Request(mode, updateId, value) { ... } var req1 = new Request(...); var req2 = new Request(...);
4.复杂的行内正则表达式
if (/r[ -~]?(?:m\d+|\d+[0-3]{2,})_m?$/.test(thing)) {
// ... wat
}
如果是多次使用的正则,应该用变量缓存起来.而且如果给它一个名称,别人还可以从变量名称上推断出这个复杂的正则到底是干什么用的.
译者注:如果是多次使用的相同的正则字面量,用变量缓存起来就只会生成一个正则对象,这样可以减少内存占用.不过ES3和ES5有点区别.
5.使用单一的字符作为变量名
这样的变量名称没有任何意义,除非使用在循环语句中,比如i作为累加器,这已经成为了语言习惯.
6.到处是严格相等
if (typeof x === 'string' && x === 'mode:45') {
// ...
}
有时候这样写是没必要的,比如上面的例子,常规的相等就已经足够了.
译者注:关于这一点,可以看一下我翻译的一篇文章什么时候能用==,里面的结论是:任何时候都应该使用严格相等.
7.把真假性当成存在性
if (!cachedReturnValues[key]) {
cachedReturnValues[key] = value;
}
上面的代码有潜在的危险.更好的办法是使用‘in’运算符来证明存在性,必要的时候还要配合‘hasOwnProperty’.
译者注:这个例子的本意是,如果不存在key属性的话,就把值value赋值给它.但如果key的值刚好为0或其他假值的话,那么判断也会成立,也就出错了.使用for in为什么要配合hasOwnProperty,请参考我的另一篇文章JavaScript中的属性:如何遍历属性
8.不加注释或者过多的注释
// 下面是一个循环
for (var i = 0; i < arr.length; i++) {
// 这里是循环体
arr[i] += 1;
} // 循环结束
过多的注释会影响代码的清晰度,也表明了你没有搞懂注释的意义.完全没有注释则表明你是一个懒惰而又自大的人.
9.认为我自己写的代码不需要单元测试
单元测试可以确保我们自己写的代码正确的实现了想要的功能.
10.使用过长的太过具体的标识符名称
过长的命名意味着糟糕的API设计.当你在定义变量/方法的时候,一定要反复思考,要使定义的变量名称即短小又有意义.那么你会得到一个更整洁的API.比如:
// 不好的:
var responseCache = {};
function generateNextIDForANewResponseCacheItem() {...}
function deleteResponseCacheItemsThatDoNotEqual(value) {...}
// 好的:
function ResponseCache() {
this.data = {};
}
ResponseCache.prototype = {
genNextID: function() {...},
remove: function(optionalFnCheck) {...}
};
11.严格遵循标准
这么做并不会解决那些你认为它可以为你解决的一些问题.正确的做法是:自己思考一下到底什么是正确的什么是错误的.这也就是我为什么要防止自己过度热衷于那些代码风格以及其他相关规定的原因.
译者注:如果是团队合作,指公司项目或者开源项目,那么最好遵循大部分通用的代码风格和规则.如果是自己一个人维护的代码,你想怎么写都可以.