- Programs must communicate clearly to people.
- Good use of style can help reduce the occurrence of errors.
- A good style can help produce better programs.
- Designing a programming style demands discipline.
編譯器的規(guī)范叫做 “語法規(guī)則(grammar)”孝治,那么程序員的 “規(guī)范” 就應(yīng)該稱作 “編程風(fēng)格(programming style)”舰罚。
好的編程風(fēng)格應(yīng)該考慮到如何使代碼清晰易讀、減少出錯(cuò)式镐。尤其是對(duì)于JavaScript這種語法自由度很高的語言涛癌,塑造一個(gè)好的編程風(fēng)格有助于寫出質(zhì)量更高犯戏、錯(cuò)誤更少、更易于維護(hù)的JavaScript程序拳话。
1. 縮進(jìn)
空格和Tab鍵先匪,都可以產(chǎn)生縮進(jìn)效果(intent)。
Tab鍵可以節(jié)省擊鍵次數(shù)弃衍,但不同的文本編輯器對(duì)Tab的顯示不盡相同呀非,有的顯示四個(gè)空格,有的顯示兩個(gè)空格镜盯,所以有人覺得岸裙,空格鍵可以使得顯示效果更統(tǒng)一。
無論你選擇哪一種方法速缆,都是可以接受的降允,要做的就是始終堅(jiān)持這一種選擇。不要一會(huì)使用Tab鍵艺糜,一會(huì)使用空格鍵剧董。
2. 區(qū)塊
如果循環(huán)和判斷的代碼體只有一行,JavaScript允許該區(qū)塊(block)省略大括號(hào)破停。
if (a)
b();
c();
上面代碼的原意可能是下面這樣翅楼。
if (a){
b();
c();
}
但是,實(shí)際效果卻是下面這樣辱挥。
if (a) {
b();
}
c();
因此犁嗅,總是使用大括號(hào)表示區(qū)塊。
另外晤碘,區(qū)塊起首的大括號(hào)的位置褂微,有許多不同的寫法。
最流行的有兩種园爷。一種是起首的大括號(hào)另起一行:
block
{
//...
}
另一種是起首的大括號(hào)跟在關(guān)鍵字的后面宠蚂。
block {
//...
}
一般來說,這兩種寫法都可以接受童社。但是求厕,JavaScript要使用后一種,因?yàn)镴avaScript會(huì)自動(dòng)添加句末的分號(hào),導(dǎo)致一些難以察覺的錯(cuò)誤呀癣。
return
{
key: value
};
上面的代碼的原意美浦,是要返回一個(gè)對(duì)象,但實(shí)際上返回的是undefined
项栏,因?yàn)镴avaScript自動(dòng)在return語句后面添加了分號(hào)浦辨。為了避免這一類錯(cuò)誤,需要寫成下面這樣:
return {
key: value
};
因此沼沈,表示區(qū)塊起首的大括號(hào)流酬,不要另起一行。
3. 圓括號(hào)
圓括號(hào)(parentheses)在JavaScript中有兩種作用列另,一種表示函數(shù)的調(diào)用芽腾,另一種表示表達(dá)式的組合(grouping)。
// 圓括號(hào)表示函數(shù)的調(diào)用
console.log('abc');
// 圓括號(hào)表示表達(dá)式的組合
(1 + 2) * 3
我們可以用空格页衙,區(qū)分這兩種不同的括號(hào)摊滔。以下是關(guān)于空格
的一些規(guī)則:
- 表示函數(shù)調(diào)用時(shí),函數(shù)名與左括號(hào)之間沒有空格店乐。
- 表示函數(shù)定義時(shí)惭载,函數(shù)名與左括號(hào)之間沒有空格。
- 其他情況時(shí)响巢,前面位置的語法元素與左括號(hào)之間,都有一個(gè)空格棒妨。
按照上面的規(guī)則踪古,下面的寫法都是不規(guī)范的。
foo (bar)return(a+b);
if(a === 0) {...}
function foo (b) {...}
function(x) {...}
上面代碼的最后一行是一個(gè)匿名函數(shù)券腔,function
是語法關(guān)鍵字伏穆,不是函數(shù)名,所以與左括號(hào)之間應(yīng)該要有一個(gè)空格纷纫。
4. 行尾的分號(hào)
4.1 不使用分號(hào)的情況
分號(hào)表示一條語句的結(jié)束枕扫。有一些語法結(jié)構(gòu)不需要在語句的結(jié)尾添加分號(hào),主要是以下三種情況辱魁。
(1)for和while循環(huán)
for ( ; ; ) {
} // 沒有分號(hào)
while (true)
} // 沒有分號(hào)
需要注意的是do...while
循環(huán)是有分號(hào)的烟瞧。
do {
a--;
} while(a > 0); // 分號(hào)不能省略
(2)分支語句:if,switch染簇,try
if (true) {
} // 沒有分號(hào)
switch () {
} // 沒有分號(hào)
try {
} catch {
} // 沒有分號(hào)
(3)函數(shù)的聲明語句
function f() {
} // 沒有分號(hào)
但是函數(shù)表達(dá)式仍然要使用分號(hào)参滴。
var f = function f() {
};
以上三種情況,如果使用了分號(hào)锻弓,并不會(huì)出錯(cuò)砾赔。因?yàn)椋忉屢鏁?huì)把這個(gè)分號(hào)解釋為空語句。
4.2 分號(hào)的自動(dòng)添加
除了上一節(jié)的三種情況暴心,所有語句都應(yīng)該使用分號(hào)妓盲。但是,如果沒有使用分號(hào)专普,大多數(shù)情況下悯衬,JavaScript會(huì)自動(dòng)添加。
var a = 1
// 等同于
var a = 1;
這種語法特性被稱為“分號(hào)的自動(dòng)添加”(Automatic Semicolon Insertion脆诉,簡稱ASI)甚亭。
因此,有人提倡省略句尾的分號(hào)击胜。麻煩的是亏狰,如果下一行的開始可以與本行的結(jié)尾連在一起解釋,JavaScript就不會(huì)自動(dòng)添加分號(hào)偶摔。
// 等同于 var a = 3
var
a
=
3
// 等同于 'abc'.length
'abc'
.length
// 等同于 return a + b;
return a +
b;
// 等同于 obj.foo(arg1, arg2);
obj.foo(arg1,
arg2);
// 等同于 3 * 2 + 10 * (27 / 6)
3 * 2
+
10 * (27 / 6)
上面代碼都會(huì)多行放在一起解釋暇唾,不會(huì)每一行自動(dòng)添加分號(hào)。這些例子還是比較容易看出來的辰斋,但是下面這個(gè)例子就不那么容易看出來了策州。
x = y(function () {
// ...
})();
// 等同于
x = y(function () {...})();
下面是更多不會(huì)自動(dòng)添加分號(hào)的例子。
// 解釋為 c(d+e)
var a = b + c(d+e).toString();
// 解釋為 a = b/hi/g.exec(c).map(d)
// 正則表達(dá)式的斜杠宫仗,會(huì)當(dāng)作除法運(yùn)算符
a = b
/hi/g.exec(c).map(d);
// 解釋為'b'['red', 'green']够挂,
// 即把字符串當(dāng)作一個(gè)數(shù)組,按索引取值
var a = 'b'
['red', 'green'].forEach(function (c) {
console.log(c);
})
// 解釋為 function(x) { return x }(a++)
// 即調(diào)用匿名函數(shù)藕夫,結(jié)果f等于0
var a = 0;
var f = function(x) { return x }
(a++)
一般來說孽糖,在沒有分號(hào)結(jié)尾的情況下,如果本行的結(jié)尾或者下一行起首的毅贮,是(办悟、[、+滩褥、-病蛉、/這五個(gè)字符中的一個(gè),分號(hào)不會(huì)被自動(dòng)添加瑰煎。
只有下一行的開始與本行的結(jié)尾铺然,無法放在一起解釋,JavaScript引擎才會(huì)自動(dòng)添加分號(hào)酒甸。
if (a < 0) a = 0
console.log(a)
// 等同于下面的代碼探熔, 因?yàn)?console沒有意義
if (a < 0) a = 0;
console.log(a)
另外,如果一行的起首是 “自增”(++
)或 “自減”(--
)運(yùn)算符烘挫,則它們的前面會(huì)自動(dòng)添加分號(hào)诀艰。
a = b = c = 1
a
++
b
--
c
console.log(a, b, c)
// 1 2 0
上面代碼 之所以會(huì)得到“1 2 0”的結(jié)果柬甥,原因是自增和自減運(yùn)算符前,自動(dòng)加上了分號(hào)其垄。上面的代碼實(shí)際上等同于下面的形式苛蒲。
a = b = c = 1;
a;
++b;
--c;
如果continue
、break
绿满、return
和throw
這四個(gè)語句后面臂外,直接跟換行符,則會(huì)自動(dòng)添加分號(hào)喇颁。這意味著漏健,如return
語句返回的是一個(gè)對(duì)象的字面量,起首的大括號(hào)一定要寫在同一行橘霎,否則得不到預(yù)期結(jié)果蔫浆。
return
{ first: 'Jane' };
// 解釋成
return;
{ first: 'Jane' };
由于解釋引擎自動(dòng)添加分號(hào)的行為難以預(yù)測,因此編寫代碼的時(shí)候不應(yīng)該省略行尾的分號(hào)姐叁。
不應(yīng)該省略結(jié)尾的分號(hào)瓦盛,還有一個(gè)原因。有些JavaScript代碼壓縮器不會(huì)自動(dòng)添加分號(hào)外潜,因此遇到?jīng)]有分號(hào)的結(jié)尾原环,就會(huì)讓代碼保持原狀,而不是壓縮成一行处窥,使得壓縮無法得到最優(yōu)的結(jié)果嘱吗。
另外,不寫結(jié)尾的分號(hào)滔驾,可能會(huì)導(dǎo)致腳本合并出錯(cuò)柜与。所以,有的代碼庫在第一行語句開始前嵌灰,會(huì)加上一個(gè)分號(hào)。
;var a = 1;
// ...
上面這種寫法就可以避免與其他腳本合并時(shí)颅悉,排在前面的腳本最后一行語句沒有分號(hào)沽瞭,導(dǎo)致運(yùn)行出錯(cuò)的問題。
5. 全局變量
JavaScript 最大的語法缺點(diǎn)剩瓶,可能就是全局變量對(duì)于任何一個(gè)代碼塊驹溃,都是可讀可寫。這對(duì)代碼的模塊化和重復(fù)使用延曙,非常不利豌鹤。
因此,避免使用全局變量枝缔。如果不得不使用布疙,用大寫字母表示變量名蚊惯,比如UPPER_CASE
。
6. 變量聲明
JavaScript會(huì)自動(dòng)將變量聲明"提升"(hoist)到代碼塊(block)的頭部灵临。
if (!o) {
var o = {};
}
// 等同于
var o;
if (!o) {
o = {};
}
為了避免可能出現(xiàn)的問題截型,最好把變量聲明都放在代碼塊的頭部。
for (var i = 0; i < 10; i++) {
// ...
}
// 寫成
var i;
for (i = 0; i < 10; i++) {
// ...
}
另外儒溉,所有函數(shù)都應(yīng)該在使用之前定義宦焦,函數(shù)內(nèi)部的變量聲明,都應(yīng)該放在函數(shù)的頭部顿涣。
7. new命令
JavaScript使用new
命令波闹,從構(gòu)造函數(shù)生成一個(gè)新對(duì)象。
var o = new myObject();
上面這種做法的問題是涛碑,一旦你忘了加上new
精堕,myObject()
內(nèi)部的this
關(guān)鍵字就會(huì)指向全局對(duì)象,導(dǎo)致所有綁定在this
上面的變量锌唾,都變成全局變量锄码。
因此,建議使用Object.create()
命令晌涕,替代new
命令滋捶。如果不得不使用new
,為了防止出錯(cuò)余黎,最好在視覺上把構(gòu)造函數(shù)與其他函數(shù)區(qū)分開來重窟。比如,構(gòu)造函數(shù)的函數(shù)名惧财,采用首字母大寫(InitialCap)巡扇,其他函數(shù)名一律首字母小寫。
8. with語句
with
可以減少代碼的書寫垮衷,但是會(huì)造成混淆厅翔。
with (o) {
foo = bar;
}
上面的代碼,可以有四種運(yùn)行結(jié)果:
o.foo = bar;
o.foo = o.bar;
foo = bar;
foo = o.bar;
這四種結(jié)果都可能發(fā)生搀突,取決于不同的變量是否有定義刀闷。因此,不要使用with
語句仰迁。
9. 相等和嚴(yán)格相等
JavaScript有兩個(gè)表示"相等"的運(yùn)算符:"相等"(==
)和"嚴(yán)格相等"(===
)甸昏。
因?yàn)?相等"運(yùn)算符會(huì)自動(dòng)轉(zhuǎn)換變量類型,造成很多意想不到的情況:
0 == ''// true
1 == true // true
2 == true // false
0 == '0' // true
false == 'false' // false
false == '0' // true
’ \t\r\n ' == 0 // true
因此徐许,不要使用“相等”(==
)運(yùn)算符施蜜,只使用“嚴(yán)格相等”(===
)運(yùn)算符。
10. 語句的合并
有些程序員追求簡潔雌隅,喜歡合并不同目的的語句翻默。比如缸沃,原來的語句是
a = b;
if (a) {
// ...
}
他喜歡寫成下面這樣。
if (a = b) {
// ...
}
雖然語句少了一行冰蘑,但是可讀性大打折扣和泌,而且會(huì)造成誤讀,讓別人誤解這行代碼的意思是下面這樣祠肥。
if (a === b){
// ...
}
建議不要將不同目的的語句武氓,合并成一行。
11. 自增和自減運(yùn)算符
自增(++
)和自減(--
)運(yùn)算符仇箱,放在變量的前面或后面县恕,返回的值不一樣,很容易發(fā)生錯(cuò)誤剂桥。事實(shí)上忠烛,所有的++
運(yùn)算符都可以用+= 1
代替。
++x
// 等同于
x += 1;
改用+= 1
权逗,代碼變得更清晰了美尸。有一個(gè)很可笑的例子,某個(gè)JavaScript函數(shù)庫的源代碼中出現(xiàn)了下面的片段:
++x;
++x;
這個(gè)程序員忘了斟薇,還有更簡單师坎、更合理的寫法。
x += 2;
建議自增(++
)和自減(--
)運(yùn)算符盡量使用+=
和-=
代替堪滨。
12. switch...case結(jié)構(gòu)
switch...case
結(jié)構(gòu)要求胯陋,在每一個(gè)case
的最后一行必須是break
語句,否則會(huì)接著運(yùn)行下一個(gè)case
袱箱。這樣不僅容易忘記遏乔,還會(huì)造成代碼的冗長。
而且发笔,switch...case
不使用大括號(hào)盟萨,不利于代碼形式的統(tǒng)一。此外了讨,這種結(jié)構(gòu)類似于goto
語句捻激,容易造成程序流程的混亂,使得代碼結(jié)構(gòu)混亂不堪量蕊,不符合面向?qū)ο缶幊痰脑瓌t。
function doAction(action) {
switch (action) {
case 'hack':
return 'hack';
break;
case 'slash':
return 'slash';
break;
case 'run':
return 'run';
break;
default:
throw new Error('Invalid action.');
}
}
上面的代碼建議改寫成對(duì)象結(jié)構(gòu)艇挨。
function doAction(action) {
var actions = {
'hack': function () {
return 'hack';
},
'slash': function () {
return 'slash';
},
'run': function () {
return 'run';
}
};
if (typeof actions[action] !== 'function') {
throw new Error('Invalid action.');
}
return actions[action]();
}
建議避免使用switch...case
結(jié)構(gòu)残炮,用對(duì)象結(jié)構(gòu)代替。
參考鏈接
- [1] JavaScript Standards Reference Guide JavaScript 標(biāo)準(zhǔn)參考教程(alpha) _阮一峰