本文主要內容來自阮一峯《JavaScript標準參考教程》javascript
「編程風格」(programming style)指的是編寫代碼的樣式規則。不一樣的程序員,每每有不一樣的編程風格。java
有人說,編譯器的規範叫作「語法規則」(grammar),這是程序員必須遵照的;而編譯器忽略的部分,就叫「編程風格」(programming style),這是程序員能夠自由選擇的。這種說法不徹底正確,程序員當然能夠自由選擇編程風格,可是好的編程風格有助於寫出質量更高、錯誤更少、更易於維護的程序。程序員
因此,編程風格的選擇不該該基於我的愛好、熟悉程度、打字量等因素,而要考慮如何儘可能使代碼清晰易讀、減小出錯。你選擇的,不是你喜歡的風格,而是一種可以清晰表達你的意圖的風格。這一點,對於 JavaScript 這種語法自由度很高的語言尤爲重要。正則表達式
必須牢記的一點是,若是你選定了一種「編程風格」,就應該堅持遵照,切忌多種風格混用。若是你加入他人的項目,就應該遵照現有的風格。編程
行首的空格和 Tab 鍵,均可以產生代碼縮進效果(indent)。數組
Tab 鍵能夠節省擊鍵次數,但不一樣的文本編輯器對 Tab 的顯示不盡相同,有的顯示四個空格,有的顯示兩個空格,因此有人以爲,空格鍵可使得顯示效果更統一。編輯器
不管你選擇哪種方法,都是能夠接受的,要作的就是始終堅持這一種選擇。不要一會使用 Tab 鍵,一會使用空格鍵。模塊化
若是循環和判斷的代碼體只有一行,JavaScript 容許該區塊(block)省略大括號。函數
if (a)
b();
c();
複製代碼
上面代碼的原意多是下面這樣。ui
if (a) {
b();
c();
}
複製代碼
可是,實際效果倒是下面這樣。
if (a) {
b();
}
c();
複製代碼
所以,建議老是使用大括號表示區塊。
另外,區塊起首的大括號的位置,有許多不一樣的寫法。最流行的有兩種,一種是起首的大括號另起一行。
block
{
// ...
}
複製代碼
另外一種是起首的大括號跟在關鍵字的後面。
block {
// ...
}
複製代碼
通常來講,這兩種寫法均可以接受。可是,JavaScript 要使用後一種,由於 JavaScript 會自動添加句末的分號,致使一些難以察覺的錯誤。
return
{
key: value
};
// 至關於
return;
{
key: value
};
複製代碼
上面的代碼的原意,是要返回一個對象,但實際上返回的是undefined
,由於 JavaScript 自動在return
語句後面添加了分號。爲了不這一類錯誤,須要寫成下面這樣。
return {
key : value
};
複製代碼
所以,表示區塊起首的大括號,不要另起一行。
圓括號(parentheses)在 JavaScript 中有兩種做用,一種表示函數的調用,另外一種表示表達式的組合(grouping)。
// 圓括號表示函數的調用
console.log('abc');
// 圓括號表示表達式的組合
(1 + 2) * 3
複製代碼
建議能夠用空格,區分這兩種不一樣的括號。
- 表示函數調用時,函數名與左括號之間沒有空格。
- 表示函數定義時,函數名與左括號之間沒有空格。
- 其餘狀況時,前面位置的語法元素與左括號之間,都有一個空格。
按照上面的規則,下面的寫法都是不規範的。
foo (bar)
return(a+b);
if(a === 0) {...}
function foo (b) {...}
function(x) {...}
複製代碼
上面代碼的最後一行是一個匿名函數,function
是語法關鍵字,不是函數名,因此與左括號之間應該要有一個空格。
首先,如下三種狀況,語法規定原本就不須要在結尾添加分號。
(1)for 和 while 循環
for ( ; ; ) {
} // 沒有分號
while (true) {
} // 沒有分號
複製代碼
注意,do...while
循環是有分號的。
do {
a--;
} while(a > 0); // 分號不能省略
複製代碼
(2)分支語句:if,switch,try
if (true) {
} // 沒有分號
switch () {
} // 沒有分號
try {
} catch {
} // 沒有分號
複製代碼
(3)函數的聲明語句
function f() {
} // 沒有分號
複製代碼
注意,函數表達式仍然要使用分號。
var f = function f() {
};j
複製代碼
以上三種狀況,若是使用了分號,並不會出錯。由於,解釋引擎會把這個分號解釋爲空語句。
除了上一節的三種狀況,全部語句都應該使用分號。可是,若是沒有使用分號,大多數狀況下,JavaScript 會自動添加。
var a = 1
// 等同於
var a = 1;
複製代碼
這種語法特性被稱爲「分號的自動添加」(Automatic Semicolon Insertion,簡稱 ASI)。
所以,有人提倡省略句尾的分號。麻煩的是,若是下一行的開始能夠與本行的結尾連在一塊兒解釋,JavaScript 就不會自動添加分號。
// 等同於 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)
複製代碼
上面代碼都會多行放在一塊兒解釋,不會每一行自動添加分號。這些例子仍是比較容易看出來的,可是下面這個例子就不那麼容易看出來了。
x = y
(function () {
// ...
})();
// 等同於
x = y(function () {...})();
複製代碼
下面是更多不會自動添加分號的例子。
// 引擎解釋爲 c(d+e)
var a = b + c
(d+e).toString();
// 引擎解釋爲 a = b/hi/g.exec(c).map(d)
// 正則表達式的斜槓,會看成除法運算符
a = b
/hi/g.exec(c).map(d);
// 解釋爲'b'['red', 'green'],
// 即把字符串看成一個數組,按索引取值
var a = 'b'
['red', 'green'].forEach(function (c) {
console.log(c);
})
// 解釋爲 function (x) { return x }(a++)
// 即調用匿名函數,結果f等於0
var a = 0;
var f = function (x) { return x }
(a++)
複製代碼
只有下一行的開始與本行的結尾,沒法放在一塊兒解釋,JavaScript 引擎纔會自動添加分號。
if (a < 0) a = 0
console.log(a)
// 等同於下面的代碼,
// 由於 0console 沒有意義
if (a < 0) a = 0;
console.log(a)
複製代碼
另外,若是一行的起首是「自增」(++
)或「自減」(--
)運算符,則它們的前面會自動添加分號。
a = b = c = 1
a
++
b
--
c
console.log(a, b, c)
// 1 2 0
複製代碼
上面代碼之因此會獲得1 2 0
的結果,緣由是自增和自減運算符前,自動加上了分號。上面的代碼實際上等同於下面的形式。
a = b = c = 1;
a;
++b;
--c;
複製代碼
若是continue
、break
、return
和throw
這四個語句後面,直接跟換行符,則會自動添加分號。這意味着,若是return
語句返回的是一個對象的字面量,起首的大括號必定要寫在同一行,不然得不到預期結果。
return
{ first: 'Jane' };
// 解釋成
return;
{ first: 'Jane' };
複製代碼
因爲解釋引擎自動添加分號的行爲難以預測,所以編寫代碼的時候不該該省略行尾的分號。
不該該省略結尾的分號,還有一個緣由。有些 JavaScript 代碼壓縮器(uglifier)不會自動添加分號,所以遇到沒有分號的結尾,就會讓代碼保持原狀,而不是壓縮成一行,使得壓縮沒法獲得最優的結果。
另外,不寫結尾的分號,可能會致使腳本合併出錯。因此,有的代碼庫在第一行語句開始前,會加上一個分號。
;var a = 1;
// ...
複製代碼
上面這種寫法就能夠避免與其餘腳本合併時,排在前面的腳本最後一行語句沒有分號,致使運行出錯的問題。
JavaScript 最大的語法缺點,可能就是全局變量對於任何一個代碼塊,都是可讀可寫。這對代碼的模塊化和重複使用,很是不利。
所以,建議避免使用全局變量。若是不得不使用,能夠考慮用大寫字母表示變量名,這樣更容易看出這是全局變量,好比UPPER_CASE
。
JavaScript 會自動將變量聲明「提高」(hoist)到代碼塊(block)的頭部。
if (!x) {
var x = {};
}
// 等同於
var x;
if (!x) {
x = {};
}
複製代碼
這意味着,變量x
是if
代碼塊以前就存在了。爲了不可能出現的問題,最好把變量聲明都放在代碼塊的頭部。
for (var i = 0; i < 10; i++) {
// ...
}
// 寫成
var i;
for (i = 0; i < 10; i++) {
// ...
}
複製代碼
上面這樣的寫法,就容易看出存在一個全局的循環變量i
。
另外,全部函數都應該在使用以前定義。函數內部的變量聲明,都應該放在函數的頭部。
with
能夠減小代碼的書寫,可是會形成混淆。
with (o) {
 foo = bar;
}
複製代碼
上面的代碼,能夠有四種運行結果:
o.foo = bar;
o.foo = o.bar;
foo = bar;
foo = o.bar;
複製代碼
這四種結果均可能發生,取決於不一樣的變量是否有定義。所以,不要使用with
語句。
JavaScript 有兩個表示相等的運算符:「相等」(==
)和「嚴格相等」(===
)。
相等運算符會自動轉換變量類型,形成不少意想不到的狀況。
0 == ''// true
1 == true // true
2 == true // false
0 == '0' // true
false == 'false' // false
false == '0' // true
' \t\r\n ' == 0 // true
複製代碼
所以,建議不要使用相等運算符(==
),只使用嚴格相等運算符(===
)。
有些程序員追求簡潔,喜歡合併不一樣目的的語句。好比,原來的語句是
a = b;
if (a) {
// ...
}
複製代碼
他喜歡寫成下面這樣。
if (a = b) {
// ...
}
複製代碼
雖然語句少了一行,可是可讀性大打折扣,並且會形成誤讀,讓別人誤解這行代碼的意思是下面這樣。
if (a === b){
// ...
}
複製代碼
建議不要將不一樣目的的語句,合併成一行。
自增(++
)和自減(--
)運算符,放在變量的前面或後面,返回的值不同,很容易發生錯誤。事實上,全部的++
運算符均可以用+= 1
代替。
++x
// 等同於
x += 1;
複製代碼
改用+= 1
,代碼變得更清晰了。
建議自增(++
)和自減(--
)運算符儘可能使用+=
和-=
代替。
switch...case
結構要求,在每個case
的最後一行必須是break
語句,不然會接着運行下一個case
。這樣不只容易忘記,還會形成代碼的冗長。
並且,switch...case
不使用大括號,不利於代碼形式的統一。此外,這種結構相似於goto
語句,容易形成程序流程的混亂,使得代碼結構混亂不堪,不符合面向對象編程的原則。
function doAction(action) {
switch (action) {
case 'hack':
return 'hack';
case 'slash':
return 'slash';
case 'run':
return 'run';
default:
throw new Error('Invalid action.');
}
}
複製代碼
上面的代碼建議改寫成對象結構。
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
結構能夠用對象結構代替。
13.參考連接