來源:阮一峰
Douglas Crockford是Javascript權威,Json格式就是他的發明。
去年11月他有一個演講(Youtube),談到了好的Javascript程式設計風格是什麼。
我非常推薦這個演講,它不僅有助於學習Javascript,而且能讓你心情舒暢,因為Crockford講得很幽默,時不時讓聽眾會心一笑。
下面,我根據這個演講和Crockford編寫的程式碼規範,總結一下”Javascript程式設計風格”。
所謂”程式設計風格”(programming style),指的是編寫程式碼的樣式規則。不同的程式設計師,往往有不同的程式設計風格。
有人說,編譯器的規範叫做”語法規則”(grammar),這是程式設計師必須遵守的;而編譯器忽略的部分,就叫”程式設計風格”(programming style),這是程式設計師可以自由選擇的。這種說法不完全正確,程式設計師固然可以自由選擇程式設計風格,但是好的程式設計風格有助於寫出質量更高、錯誤更少、更易於維護的程式。
所以,有一點應該明確,“程式設計風格”的選擇不應該基於個人愛好、熟悉程度、打字工作量等因素,而要考慮如何儘量使程式碼清晰易讀、減少出錯。你選擇的,不是你喜歡的風格,而是一種能夠清晰表達你的意圖的風格。這一點,對於Javascript這種語法自由度很高、設計不完全成熟的語言尤其重要。
一、大括號的位置
絕大多數的程式語言,都用大括號({})表示區塊(block)。起首的大括號的位置,有許多不同的寫法。
最流行的有兩種。一種是起首的大括號另起一行:
1 2 3 4 |
block { ... } |
另一種是起首的大括號跟在關鍵字的後面:
1 2 3 |
block { ... } |
一般來說,這兩種寫法都可以接受。但是,Javascript要使用後一種,因為Javascript會自動新增句末的分號,導致一些難以察覺的錯誤。
1 2 3 4 |
return { key:value; }; |
上面的程式碼的原意,是要返回一個物件,但實際上返回的是undefined,因為Javascript自動在return語句後面新增了分號。為了避免這一類錯誤,需要寫成下面這樣:
1 2 3 |
return { key : value; }; |
因此,
規則1:表示區塊起首的大括號,不要另起一行。
二、 圓括號
圓括號(parentheses)在Javascript中有兩種作用,一種表示呼叫函式,另一種表示不同的值的組合(grouping)。我們可以用空格,區分這兩種不同的括號。
規則2:呼叫函式的時候,函式名與左括號之間沒有空格。
規則3:函式名與引數序列之間,沒有空格。
規則4:所有其他語法元素與左括號之間,都有一個空格。
按照上面的規則,下面的寫法都是不規範的:
1 2 3 4 5 |
foo (bar) return(a+b); if(a === 0) {...} function foo (b) {...} function(x) {...} |
三、分號
分號表示語句的結束。大多數情況下,如果你省略了句尾的分號,Javascript會自動新增。
1 |
var a = 1 |
等同於
1 |
var a = 1; |
因此,有人提倡省略句尾的分號。但麻煩的是,如果下一行的第一個字元(token)是下面這五個字元之一,Javascript將不對上一行句尾新增分號:”(“、”[“、”/”、”+”和”-“。
1 2 3 4 |
x = y (function (){ ... })(); |
上面的程式碼等同於
1 |
x = y(function (){...})(); |
因此,
規則5:不要省略句末的分號。
四、with語句
with可以減少程式碼的書寫,但是會造成混淆。
1 2 3 |
with (o) { foo = bar; } |
上面的程式碼,可以有四種執行結果:
1 2 3 4 |
o.foo = bar; o.foo = o.bar; foo = bar; foo = o.bar; |
這四種結果都可能發生,取決於不同的變數是否有定義。因此,
規則6:不要使用with語句。
五、相等和嚴格相等
Javascript有兩個表示”相等”的運算子:”相等”(==)和”嚴格相等”(===)。
因為”相等”運算子會自動轉換變數型別,造成很多意想不到的情況:
1 2 3 4 5 6 7 |
0 == ''// true 1 == true // true 2 == true // false 0 == '0' // true false == 'false' // false false == '0' // true " \t\r\n " == 0 // true |
因此,
規則7:不要使用”相等”(==)運算子,只使用”嚴格相等”(===)運算子。
六、語句的合併
有些程式設計師追求簡潔,喜歡合併不同目的的語句。比如,原來的語句是
1 2 |
a = b; if (a) {...} |
他喜歡寫成下面這樣:
1 |
if (a = b) {...} |
雖然語句少了一行,但是可讀性大打折扣,而且會造成誤讀,讓別人誤以為這行程式碼的意思是:
1 |
if (a === b){...} |
另外一種情況是,有些程式設計師喜歡在同一行中賦值多個變數:
1 |
var a = b = 0; |
他以為,這行程式碼等同於
1 |
var a = 0, b = 0; |
實際上不是,它的真正效果是下面這樣:
1 2 |
b = 0; var a = b; |
因此,
規則8:不要將不同目的的語句,合併成一行。
七、變數宣告
Javascript會自動將變數宣告”提升”(hoist)到程式碼塊(block)的頭部。
1 2 3 |
if (!o) { var o = {}; } |
等同於
1 2 3 4 |
var o; if (!o) { o = {}; } |
為了避免可能出現的問題,不如把變數宣告都放在程式碼塊的頭部。
1 |
for (var i ...) {...} |
最好寫成:
1 2 |
var i; for (i ...) {...,} |
因此,
規則9:所有變數宣告都放在函式的頭部。
規則10:所有函式都在使用之前定義。
八、全域性變數
Javascript最大的語法缺點,可能就是全域性變數對於任何一個程式碼塊,都是可讀可寫。這對程式碼的模組化和重複使用,非常不利。
規則11:避免使用全域性變數;如果不得不使用,用大寫字母表示變數名,比如UPPER_CASE。
九、new命令
Javascript使用new命令,從建構函式生成一個新物件。
1 |
var o = new myObject(); |
這種做法的問題是,一旦你忘了加上new,myObject()內部的this關鍵字就會指向全域性物件,導致所有繫結在this上面的變數,都變成全部變數。
規則12:不要使用new命令,改用Object.create()命令。
如果不得不使用new,為了防止出錯,最好在視覺上把建構函式與其他函式區分開來。
規則13:建構函式的函式名,採用首字母大寫(InitialCap);其他函式名,一律首字母小寫。
十、自增和自減運算子
自增(++)和自減(–)運算子,放在變數的前面或後面,返回的值不一樣,很容易發生錯誤。
事實上,所有的++運算子都可以用”+= 1″代替。
1 |
++x |
等同於
1 |
x += 1; |
程式碼變得更清晰了。有一個很可笑的例子,某個Javascript函式庫的原始碼中出現了下面的片段:
1 2 |
++x; ++x; |
這個程式設計師忘了,還有更簡單、更合理的寫法:
1 |
x += 2; |
因此,
規則14:不要使用自增(++)和自減(–)運算子,用+=和-=代替。
十一、區塊
如果迴圈和判斷的程式碼體只有一行,Javascript允許該區塊(block)省略大括號。
下面的程式碼
1 |
if (a) b(); c(); |
原意可能是
1 |
if (a) { b(); c();} |
但是,實際效果是
1 |
if (a) { b();} c(); |
因此,
規則15:總是使用大括號表示區塊。
(完)