閱讀 code 的時間會比 撰寫 的時間來得多,因此寫出容易維護的 code 便顯得相當重要。
容易維護的 code 具備以下條件:

  • 可讀性
  • 一致性(像同一個人寫的)
  • 可預期的
  • 文件化

少用全域變數(global variable)

  • 變數的 scope 在 function 內,即 local variable
  • 定義在 function 外或未經宣告直接使用(不論 function 內外)則為 global
  • JavaScript 執行環境皆有 global object,在 function 外可用 this 進行存取;在 browser 中,window 為 global object 的一個 property,指向 global object 本身,因此也可用 window 存取 global variable
1
2
3
4
5
gvar = "test"; // antipattern
console.log(gvar); // "test"
console.log(window.gvar); // "test"
console.log(window["gvar"]); // "test"
console.log(this.gvar); // "test"
1
2
3
4
// 如果在不同執行環境中,`window` 未必可用,若不寫死可用以下方法
var global = (function () {
return this;
}());
  • 使用 global variable 可能發生命名衝突(例如有多個 libraries 或多段不同人寫的 code 時),應盡可能少用
  • chain assignment 也會建立 global variable
1
2
3
4
5
function foo() {
var a = b = 0; // a: local; b: global
var c, d;
c = d = 0; // c: local; d: local
}
  • 隱含的 global variable(即未經 var 宣告就直接使用的)可以用 delete 刪除;因為該變數嚴格來說其實是 global object 的 property 而非變數,delete 可刪除 object 的 property 而不能刪除變數

使用單一 var

  • hoisting 行為:在任何位置以 var 宣告變數,其行為與「在頂端宣告」相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bar = "global"; // global variable
function foo() {
console.log(bar); // "undefined"
var bar = "local";
console.log(bar); // "local"
}
foo();
// 上例相當於以下行為
bar = "global"; // global variable
function foo() {
var bar; // same as: var bar = undefined;
console.log(bar); // "undefined"
bar = "local";
console.log(bar); // "local"
}
foo();
  • 在 function 開頭使用單一 var 敘述,優點:
    • 查閱使用變數時只需找一個地方
    • 避免 hoisting 行為造成的邏輯錯誤
    • 減少「造成隱含的全域變數」發生
    • 使 code 字數更精簡
1
2
3
4
5
6
7
8
9
10
function foo() {
var a = 0,
b = 1,
sum = a + b,
myobj = {},
i,
j,
result = document.getElementById("result"),
resultStyle = result.style;
}

預先計算 iteration 次數

  • 使用 for loop 時預先計算次數,以避免每次皆計算而影響效能(尤其針對 HTMLCollection object 時影響更大)
1
2
3
for (var i = 0, max = myarray.length; i < max; i++) {
// ...
}
  • 效能更佳的 loop(只使用一個變數且只判斷零與非零)
1
2
3
4
5
6
7
8
9
10
11
12
// for-loop
var i, myarray = [];
for (i = myarray.length; i--;) {
// ...
}
// while-loop
var myarray = [],
i = myarray.length;
while (i--) {
// ...
}
// 註:JSLint 看到 ++ 或 -- 會抱怨
  • for-in loop 不保證列出的順序,建議將 for 用於重複 array,將 for-in 用於重複 object
  • 使用 for-in 時應加上 hasOwnProperty() 檢查該 property 是否來自 prototype,因為擴充 prototype 的行為是 live,所有已存在的 object 將自動能存取新加入的 property
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var dog = {
eye: 2,
head: 1,
leg: 4
}
// 在某處將一個新 method 加入到所有 object 中
if (typeof Object.prototype.clone === "undefined") {
Object.prototype.clone = function () {};
}
// 1. for-in loop
for (var i in dog) {
// 兩種用法皆可
// if (Object.prototype.hasOwnProperty.call(dog, i)) {
if (dog.hasOwnProperty(i)) {
console.log(i, ":", dog[i]);
}
}
/*
console 中的結果:
eye : 2
head : 1
leg : 4
\*/
// 2. antipattern (no hasOwnProperty())
for (var i in dog) {
console.log(i, ":", dog[i]);
}
/*
console 中的結果:
eye : 2
head : 1
leg : 4
clone : function () {}
*/

Coding Conventions

  • 縮排很重要
  • 永遠都應加上大括號,即使只是單行敘述
  • 左大括號置於同一行
  • 適當地加入空格
  • 以空白行分隔不同段落

Naming Conventions

  • 將 constructor function 首字使用大寫,一般 function 則用小寫
  • 駝峰式命名法:僅每個字的第一個字母使用大寫,其餘皆小寫
  • 常數採用全大寫命名;全域變數也可考慮使用全大寫字母
  • 在變數前或後加底線來表示 private member,如 getElements_()(JSLint 會對底線前綴警告)

其他

  • 最好不要擴充內建型別(Object(), Array(), Function(), etc.)的 prototype
  • switch 中,case 不應縮排,最後務必以 break; 結束每個 case,務必以 default: 結束 switch
  • 避免造成隱含的型別轉換,應使用 ===!== 同時比較型別與值 (reference)
  • 避免使用 eval(),若非用不可時可改用 new Function() 或 immediate function
  • 為避免回傳 NaN,建議使用 parseInt() 將 string 轉成 number,且第二個參數(進位制的基數)不應省略
1
2
var month = "03";
month = parseInt(month, 10); // 10進位