原文: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.嚴格遵循標准
這么做並不會解決那些你認為它可以為你解決的一些問題.正確的做法是:自己思考一下到底什么是正確的什么是錯誤的.這也就是我為什么要防止自己過度熱衷於那些代碼風格以及其他相關規定的原因.
譯者注:如果是團隊合作,指公司項目或者開源項目,那么最好遵循大部分通用的代碼風格和規則.如果是自己一個人維護的代碼,你想怎么寫都可以.
