設計模式一直是程式員談論的“高端”話題之一,總有一種敬而遠之的心態。在瞭解後才知道在將函數作為一等對象的語言中,有許多需要利用對象多態性的設計模式,比如單例模式、 策略模式等,這些模式的結構與傳統面向對象語言的結構大相徑庭,實際上已經融入到了語言之中,我們可能經常使用它們,只是不知道它們的名字而已。 ...
sidebar: auto
sidebarDepth: 4
JavaScript編碼風格指南
內容出處:
-
Nicholas C. Zakas 《編寫可維護的JavaScript》
源文件基礎
命名
文件名必須全部小寫,並且可以包含下劃線(_
)或短劃線(-
),但不包含其他標點符號。
基本的格式化
留白
在邏輯相關的代碼塊之間添加空行可以提高代碼的可讀性。
兩行空行僅限在如下情況中使用:
- 在不同的源代碼文件之間。
- 在類和介面定義之間。
單行空行僅限在如下情況中使用:
- 方法之間。
- 方法中局部變數和第一行語句之間。
- 多行或者單行註釋之前。
- 方法中邏輯代碼塊之間以提升代碼的可讀性。
空格應當在如下情況中使用:
- 關鍵詞後跟括弧(
(
)的情況應當使用空格隔開。 - 參數列表中逗號(
,
)後應當保留一個空格。 - 所有的除了點(
.
)之外的二元運算符,其操作數都應當用空格隔開。單目運算符的操作
數之間不應該用空格隔開,如一元減號、遞增(++
)、遞減(--
)。 for
語句中的表達式之間應當用空格隔開。
縮進層級
每一行的層級由4個空格組成,避免使用製表符(Tab)進行縮進。使用空格縮進在所有
的系統和編輯器中,文件的展現格式不會有任何差異。
// 好的寫法:使用四個空格縮進
if (true) {
doSomething();
}
語句結尾
推薦總是使用分號。
// 合法代碼
var name = "Nicholas";
function sayName() {
alert(name);
}
// 合法代碼,但不推薦這樣寫
var name = "Nicholas"
function sayName() {
alert(name)
}
行的長度
每行長度不應該超過80個字元,如果一行多於80個字元,應該在一個運算符(逗號,加
號等)後換行,下一行應當增加二級縮進(8個字元)。
// 好的寫法
doSomething(argument1, argument2, argument3, argument4,
argument5);
// 不好的寫法:第二行只有四個空格的縮進
doSomething(argument1, argument2, argument3, argument4,
argument5);
// 不好的寫法:在運算符之前換行
doSomething(argument1, argument2, argument3, argument4
, argument5);
命名
變數和函數在命名時應當小心。命名僅限於數字字母字元,某些情況下也可以使用下劃線。
最好不要在任何命名中使用美元($
)或者反斜杠(\
)。
遵照駝峰式大小寫命名法。駝峰式大小寫命名法是由小寫字母開始,後續每個單詞首字
母都大寫。
// 好的寫法:使用駝峰式命名法
var thisIsMyName;
var anotherVariable;
var aVeryLongVariableName;
變數和函數
變數名應當總是遵循駝峰大小寫命名法,並且變數命名首碼應當是名詞,函數名前應當是動詞。
這樣可以將變數與函數區分開來。
// 好的寫法
var count = 10;
var myName = "badfl";
var found = true;
// 不好的寫法:變數看起來像函數
var getCount = 10;
var isFound = true;
// 好的寫法
function getName() {
return myName;
}
// 不好的寫法:函數看起來像變數
function theName() {
return myName;
}
一些使用動詞常見的約定:
動詞 | 含義 |
---|---|
can | 函數返回一個布爾值 |
has | 函數返回一個布爾值 |
is | 函數返回一個布爾值 |
get | 函數返回一個非布爾值 |
set | 函數用來保存一個值 |
// 好的命名
if (isEnabled()) {
setName("badfl");
}
if (getName() === "badfl") {
doSomething();
}
構造函數:通過new
運算符創建新對象的函數。應當以駝峰格式命名並且首字元大寫。構造函數名稱應當以非動詞開頭,因為new
代表著創建一個對象實例的操作。
// 好的寫法
function MyObject() {
// 代碼
}
// 不好的寫法:小寫字母開頭
function myObject() {
// 代碼
}
// 不好的寫法:使用下劃線
function My_Object() {
// 代碼
}
// 不好的寫法:動詞開頭
function getMyObject() {
// 代碼
}
常量
區分普通的變數和常量,常量使用大寫字母和下劃線(_
)來命名,下劃線(_
)用以分割單詞。
// 好的寫法
var MAX_COUNT = 10;
var TOTAL_COUNT = 10;
var URL = "http://www.badfl.com";
// 不好的寫法:駝峰形式
var totalCount = 10;
// 不好的寫法:混合形式
var total_COUNT = 10;
運算符間距
二元運算符(&&
和||
)前後必須使用一個空格來保持表達式的整潔。操作符包括賦值運算符和邏輯運算符。
// 好的寫法
var found = (values[i] === item);
// 好的寫法
if (found && (count > 10)) {
doSomething();
}
// 好的寫法
for (i = 0; i < count; i++) {
process(i);
}
// 不好的寫法:丟失了空格
var found = (values[i]===item);
// 不好的寫法:丟失了空格
if (found&&(count>10)) {
doSomething();
}
// 不好的寫法:丟失了空格
for (i=0; i<count; i++) {
process(i);
}
括弧間距
當使用括弧時,緊接左括弧((
)之後和緊接右括弧()
)之前不應該有空格。
// 好的寫法
var found = (values[i] === item);
// 好的寫法
if (found && (count > 10)) {
doSomething();
}
// 好的寫法
for (i = 0; i < count; i++) {
process(i);
}
// 不好的寫法:左括弧之後有額外的空格
var found = ( values[i] === item);
// 不好的寫法:右括弧之前有額外的空格
if (found && (count > 10) ) {
doSomething();
}
// 不好的寫法:參數兩邊有額外的空格
for (i = 0; i < count; i++) {
process( i );
}
直接量
JavaScript中包含了一些類型的原始值:String
、Number
、Boolean
、null
、undefined
。
同樣也包含對象直接量和數組直接量。這其中,只有布爾值是自解釋的,其他的類型或多或少
都需要思考一下它們如何才能精確地表示出來。
字元串
字元串應當始終使用單引號(避免使用雙引號,需要轉義的時候可以使用)且保持一行。避免在字元串中使用斜線
另起一行。
// 好的寫法
var name = 'Badfl';
// 好的寫法:需要轉義的時候,可以使用雙引號
var el = $("<div class='content'>")
// 不好的寫法:單引號
var name = "Badfl";
// 不好的寫法:字元串結束之前換行
var longString = '這是一個很長的故事,很長很長的故事 \
換行以後的文字';
數字
數字應當使用十進位整數,科學計數法表示整數、十六進位整數或者十進位浮點小數,小數前後應該至少保留一位數字。避免使用八進位直接量。
// 好的寫法
var count = 10;
// 好的寫法
var price = 10.0;
var price = 10.00;
// 好的寫法
var num = 0xA2;
// 好的寫法
var num = le23;
// 不好的寫法:十進位數字以小數點結尾
var price = 10.;
// 不好的寫法:十進位數字以小數點開頭
var price = .1;
// 不好的寫法:八進位(base 8)寫法已經廢棄
var num = 010;
null
特殊值null
除了下列情況下應當避免使用。
- 用來初始化一個變數,這個變數可能被賦值為一個對象。
- 用來和一個已經初始化的變數比較,這個變數可以是也可以不是一個對象。
- 當函數的參數期望是對象時,被用做參數傳入。
- 當函數的返回值期望是對象時,被用做返回值傳出。
下麵一些場景不應該使用null
。
- 不要使用
null
來檢測是否傳入了某個參數。 - 不要用
null
來檢測一個未初始化的變數。
// 好的用法
var person = null;
// 好的用法
function getPerson(){
if (condition) {
return new Person("badfl");
} else {
return null;
}
}
// 好的用法
var person = getPerson();
if (person != null) {
doSomething();
}
// 不好的寫法:用來和未初始化的變數比較
var person;
if (person != null) {
dosomething();
}
// 不好的寫法:檢測是否傳入了參數
function doSomething(arg1, arg2, arg3, arg4) {
if (arg4 != null) {
doSomethingElse();
}
}
undefined
undefined
是一個特殊值。沒有被初始化的變數都有一個初始值,即undefined
,表示這個
變數等待被賦值。儘量避免使用特殊值undefined
。判斷一個變數是否定義應當使用typeof
操作符。
// 好的寫法
if (typeof variable == "undefined") {
// do something
}
// 不好的寫法:使用undefined直接量
if (variable == undefined) {
// do something
}
對象直接量
創建對象最流行的一種做法是使用對象直接量,在直接量中寫出所有屬性,這種方式可以取
代顯示地創建Object
的實例然後添加屬性的這種做法。
// 好的寫法
var book = {
title: "badfl title",
author: "badfl"
};
// 不好的寫法
var book = new Object();
book.title = "badfl title";
book.author = "badfl";
對象直接量應當使用如下格式:
- 起始左花括弧(
{
)應當同表達式保持同一行。 - 每個屬性的名值對應應當保持一個縮進,第一個屬性應當在左花括弧(
{
)後另起一行。 - 每個屬性的名值對應當使用不包含引號的屬性名,其後緊跟一個冒號(之前不包含空格),
加個空格,而後是值。 - 倘若屬性值是函數類型,函數體應當在屬性名之下另起一行,而且其前後均應保留一個空行。
- 一組相關的屬性前後可以插入空行以提升代碼的可讀性。
- 結束的右花括弧(
}
)應當獨占一行。
// 好的寫法
var object = {
key1: value1,
key2: value2,
func: function() {
// 函數操作
},
key3: value3
}
// 不好的寫法:不恰當的縮進
var object = {
key1: value1,
key2: value2
}
// 不好的寫法:函數體周圍缺少空行
var object = {
key1: value1,
key2: value2,
func: function() {
// 函數操作
},
key3: value3
}
當對象字面量作為函數參數時,如果值是變數,起始花括弧應當同函數名在同一行。所有其餘
先前規則同樣適用。
// 好的寫法
doSomething({
key1: value1,
key2: value2
})
// 不好的寫法:所有代碼在同一行
doSomething({ key1: value1,key2: value2 })
數組直接量
和對象直接量類似,數組直接量是JavaScript中定義數組最簡潔的一種方式。不贊成顯式
地使用Array
構造函數來創建數組,比如:
// 不好的寫法
var colors = new Array("red", "green", "blue");
var numbers = new Array(1, 2, 3, 4, 5);
可以使用二個方括弧([]
)將數組初始元素括起來,來替代使用Array
構造函數的方式來創建數組。
// 好的做法
var colors = [ "red", "green", "blue" ];
var numbers = [ 1, 2, 3, 4, 5 ];
註釋
頻繁地使用註釋有助於他人理解你的代碼。如下情況應當使用註釋:
- 代碼晦澀難懂。
- 可能被認為錯誤的代碼
- 必要但並不明顯的針對特定瀏覽器的代碼。
- 對於對象、方法或者屬性,生成文檔是有必要的(使用恰當的文檔註釋)。
單行註釋
單行註釋應當用來說明一行代碼或者一組相關的代碼。單行註釋應保持以下風格:
- 雙斜線後敲入一個空格,且縮進與層級與下一行代碼保持一致。
- 註釋之前總有一個空行。
- 在代碼行尾部的註釋。代碼結束到註釋之間至少有一個縮進。
單行註釋可能有三種使用方式:
- 獨占一行的註釋,用來結束下一行代碼。
- 在代碼行的尾部的註釋,用來解釋它之前的代碼。
- 多行,用來註釋掉一個代碼塊。
// 好的寫法
if (condition) {
// 如果代碼執行到這裡,則表明通過了所有的安全檢查
allowed();
}
// 不好的寫法:註釋之前沒有空行
if (condition) {
// 如果代碼執行到這裡,則表明通過了所有的安全檢查
allowed();
}
// 不好的寫法:錯誤的縮進
if (condition) {
// 如果代碼執行到這裡,則表明通過了所有的安全檢查
allowed();
}
// 不好的寫法:這裡應當使用多行註釋
// 接下來的代碼非常複雜
// 我要詳細的說明一下
if (condition) {
// 如果代碼執行到這裡,則表明通過了所有的安全檢查
allowed();
}
對於代碼行尾單行註釋的情況,應確保代碼結尾同註釋之間至少一個縮進。
// 好的寫法
var result = something + somethingElse; // 一個代碼行尾的註釋
// 不好的寫法:代碼和註釋之間沒有足夠的空格
var result = something + somethingElse;// 一個代碼行尾的註釋
註釋一個代碼塊時連續使用單行註釋是唯一可以接受的情況。多行註釋不應當在這種情況下使用。
// 好的寫法
// if (condition) {
// doSomething();
// thenDoSomethingElse();
// }
多行註釋
多行註釋應當在代碼需要更多文字去解釋的時候使用。每個多行註釋都至少有如下三行。
- 首行僅僅包括
/*
註釋開始。該行不應當有其他文字。 - 接下來的行以
*
開頭並保持左對齊。這些行可以有文字描述。 - 最後一行以
*/
開頭並同先前行保持對齊。也不應當有其他文字。
多行註釋的首行應當保持同它描述代碼的相同層次的縮進。後續每行應當有同樣層次的縮進
並附加一個空格(為了適當保持*
字元的對齊)。每一個多行代碼之前應當預留一個空行。
// 好的寫法
if (condition) {
/*
* 如果代碼執行到這裡
* 說明通過了所有的安全檢測
*/
allowed();
}
// 不好的寫法:註釋之前無空行
if (condition) {
/*
* 如果代碼執行到這裡
* 說明通過了所有的安全檢測
*/
allowed();
}
// 不好的寫法:星號後面沒有空格
if (condition) {
/*
*如果代碼執行到這裡
*說明通過了所有的安全檢測
*/
allowed();
}
// 不好的寫法:錯誤的縮進
if (condition) {
/*
* 如果代碼執行到這裡
* 說明通過了所有的安全檢測
*/
allowed();
}
// 不好的寫法:代碼尾部註釋不要使用多行註釋格式
var result = something + somethingEles; /*somethingElse 不應當取值為null*/
語句和表達式
簡單語句
每一行最多只包含一條語句。所有簡單的語句都應該以分號(;
)結束。
// 好的寫法
count++;
a = b;
// 不好的寫法:多個表達式寫在一行
count++; a = b;
返回語句
返回語句當返回一個值的時候不應當使用圓括弧包裹,除非在某些情況下這麼做可以讓返回值更容易理解。
// 好的寫法
return;
return collection.size();
return (size > 0 ? size : defaultSize);
複合語句
複合語句是大括弧括起來的語句列表。複合語句應該保持以下風格:
- 括起來的語句應當較複合語句多縮進一個層級。
- 開始的大括弧應當在複合語句所在行的末尾;結束的大括弧應當獨占一行且同複合語句的開始保持同樣的縮進。
- 當語句是控制結構的一部分時,諸如
if
或者for
語句,所有語句都需要用大括弧括起來,也包括單個語句。這個約定使得我們更方便地添加語句而不用擔心忘記加括弧而引起bug。 - 像
if
一樣的語句開始的關鍵詞,其後應該緊跟一個空格,起始大括弧應當在空格之後。
if 語句
if
語句應當是下麵的格式
// if語句格式
if (condition) {
statements
}
if (condition) {
statements
} else {
statements
}
if (condition) {
statements
} else if {
statements
} else {
statements
}
絕不允許在if
語句中省略花括弧,else
關鍵字要與花括弧保持在同一行。
// 好的寫法
if (condition) {
doSomething();
}
// 好的寫法:else關鍵字與花括弧保持在同一行
if (condition) {
doSomething();
} else {
doSomethingElse();
}
// 不好的寫法:不恰當的空格
if(condition){
doSomething();
}
// 不好的寫法:else關鍵詞換行
if (condition) {
doSomething();
}
else
{
doSomethingElse();
}
// 不好的寫法:遺漏花括弧
if (condition)
doSomething();
// 不好的寫法:所有代碼寫在一行
if (condition) { doSomething(); }
// 不好的寫法:所有代碼寫在一行且沒有花括弧
if (condition) doSomething();
for 語句
for
類型的語句應當是下麵的格式。
// for語句格式
for (initialization; condition; update) {
statements
}
for (variable in object) {
statements
}
for
語句的初始化部分不應當有變數聲明。
// 好的寫法
var i,
len;
for (i=0, len=10; i < len; i++) {
// 代碼
}
// 不好的寫法:初始化時候聲明變數
for (var i=0, len=10; i < len; i++) {
// 代碼
}
// 不好的寫法:初始化時候聲明變數
for (var prop in object) {
// 代碼
}
for-in
迴圈是用來對實例對象和原型鏈中的鍵(key
)做遍歷的,而不是用來遍歷包含數字索引的數組。
// 好的寫法
var prop;
for (prop in object) {
console.log("Property name is "+ prop);
console.log("Property value is " + object[prop]);
}
// 不好的用法
var values = [ 1, 2, 3, 4, 5, 6, 7],
i;
for (i in values) {
process(items[i]);
}
當使用for-in
語句時,使用hasOwnProperty()
進行雙重檢查來過濾出對象的成員。
// 好的寫法
var prop;
for (prop in object) {
if (object.hasOwnProperty(prop)) {
console.log("Property name is "+ prop);
console.log("Property value is " + object[prop]);
}
}
// 好的寫法:包含原型鏈遍歷應當補充註釋
var prop;
for (prop in object) { // 包含原型鏈的遍歷
console.log("Property name is "+ prop);
console.log("Property value is " + object[prop]);
}
while 語句
while
類的語句應當是下麵的格式。
while (condition) {
statements
}
do 語句
do
類的語句應當是下麵的格式。
do {
statements
} while (condition);
switch 語句
switch
類的語句應當是如下格式。
switch (expression) {
case expression:
statements
default:
statements
}
switch
下的每個case
都應該保持一個縮進。除第一個之外包括default
在內每一個
case
都應當在之前保持一個空行。
每一組語句(除了default
)都應當以break
、return
、throw
結尾,或者用一行
註釋表示跳過。
// 好的寫法
switch (value) {
case 1:
/* 跳過 */
case 2:
doSomething();
break;
case 3:
return true;
default:
throw new Error("This shouldn't happen.");
}
如果一個switch
語句不包含default
情況,應當用一行註釋代替。
// 好的寫法
switch (value) {
case 1:
/* 跳過 */
case 2:
doSomething();
break;
case 3:
return true;
// 沒有default
}
不要定義重覆的case
分支
// 不好的寫法:重覆定義case分支
switch (value) {
case 1:
doSomething();
break;
case 1:
doSome();
break;
case 3:
return true;
}
try 語句
try
類的語句應當格式如下。
try {
statements
} catch (variable) {
statements
}
try {
statements
} catch (variable) {
statements
} finally {
statements
}
變數、函數和運算符
變數聲明
所有的變數在使用前都應當事先定義,變數定義應當放在函數開頭,每個var
關鍵字單獨聲
明一個變數,每行一個變數。不要聲明無用的變數。
變數定義時應當初始化。初始化的變數應當在未初
始化變數之前。
// 好的寫法
var count = 10;
var name = "badfl";
var found = false;
var empty;
// 不好的寫法:一個var聲明多個變數
var count = 10,
name = "badfl",
found = false,
empty;
// 不好的寫法:多個定義寫在一行
var count = 10, name = "badfl",
found = false, empty;
函數聲明
函數應當在使用前提前定義。一個不是作為方法的函數(也就是說沒有作為一個對象的屬性)
應當使用函數定義的格式(不是函數表達式和Function
構造器格式)。
- 函數名和開始圓括弧之間不應當有空格。
- 結束的圓括弧和右邊的花括弧之間應該保留一個空格
- 右側的花括弧應當同
function
關鍵字保持同一行。 - 開始和結束圓括弧之間不應該有空格。
- 參數名之間應當在逗號後保留一個空格。
- 函數體應當保持一級縮進。
// 好的寫法
function doSomething(arg1, arg2) {
return arg1 + arg2;
}
// 不好的寫法:第一行不恰當的空格
function doSomething (arg1, arg2){
return arg1 + arg2;
}
// 不好的寫法:函數表達式
var doSomething = function(arg1, arg2) {
return arg1 + arg2;
};
// 不好的寫法:左側的花括弧位置不對
function doSomething(arg1, arg2)
{
return arg1 + arg2;
}
// 錯誤的寫法:使用了Function構造器
var doSomething = new Function("arg1", "arg2", "return arg1 + arg2");
其他函數內部定義的函數應當在var
語句後立即定義。
// 好的寫法
function outer() {
var count = 10,
name = "badfl",
found = false,
empty;
function inner() {
// 代碼
}
// 調用 inner() 的代碼
}
// 不好的寫法:inner函數的定義先於變數
function outer() {
function inner() {
// 代碼
}
var count = 10,
name = "badfl",
found = false,
empty;
// 調用 inner() 的代碼
}
匿名函數可能作為方法賦值給對象,或者作為其他函數的參數。function
關鍵字同開始括
號之間不應有空格。
// 好的寫法
object.method = function() {
// 代碼
}
// 不好寫法:不正確的空格
object.method = function () {
// 代碼
}
函數聲明不應當出現在語句塊之內。比如,這段代碼就不會按照我們的意圖來執行(在不同的
瀏覽器運行效果不相同,不管condition
的計算結果如何,大多數瀏覽器會執行第二個聲明)。
// 不好的寫法
if (condition) {
function doSomething() {
alert("Hi!");
}
doSomething();
} else {
function doSomething() {
alert("Yo!");
}
doSomething();
}
函數調用間隔
函數調用時,在函數名和左括弧之間沒有空格。這樣做是為了將它與塊語句區分開來
// 好的寫法
doSomething(item);
// 不好的寫法:看起來像一個塊語句
doSomething (item);
// 用來做比對的塊語句
while (item) {
// 代碼邏輯
}
立即調用的函數
立即被調用的函數應當在函數調用的外層使用圓括弧包裹。
// 好的寫法
var value = (function() {
// 函數體
return {
message: "badfl"
}
}());
// 不好的寫法:函數調用外層沒有用圓括弧包裹
var value = function() {
// 函數體
return {
message: "badfl"
}
}();
// 不好的寫法:圓括弧位置不當
var value = (function() {
// 函數體
return {
message: "badfl"
}
})();
賦值
當變數賦值時,如果右側是含有比較語句的表達式,需要用圓括弧包裹。
// 好的寫法
var flag = (1 < count);
// 不好的寫法:遺漏圓括弧
var flag = 1 < count;
相等
使用===
(嚴格相等)和!==
(嚴格不相等)代替==
(相等)和!=
(不等)來避免弱類型轉換錯誤。
// 好的寫法
var same = (a === b);
// 不好的寫法:使用==
var same = (a == b);
三元操作符
三元操作符應當僅僅用在條件賦值語句中,而不要作為if
語句的替代品。
// 好的寫法
var value = condition ? value1 : value2;
// 不好的寫法:沒有賦值,應當使用if表達式
condition ? doSomething() : doSomethingElse();
eval()
避免使用eval()
,涉及到回調中解析JSON的情形可以使用。