你為專案所擇風格都應為最高準則。作為一個描述放置於你的專案中,並連結到這個文件作為程式碼風格一致性、可讀性和可維護性的保證。
一、空白
1.永遠不要混用空格和Tab。
2.開始一個項目,在寫程式碼之前,選擇軟縮排(空格)或 Tab(作為縮排方式),並將其作為最高準則。
a).為了可讀, 我總是推薦在你的編輯中設計2個字母寬度的縮進 — 這等同於兩個空格或者兩個空格替代一個 Tab。
3.如果你的編輯器支持,請總是打開 “顯示不可見字元” 這個設定。好處是:
a).保證一致性
b).去掉行末的空格
c).去掉空行的空格
d).提交和對比更具可讀性
二、美化文法
A. 小括號, 花括號, 換行
// 2.A.1.1
// 難辨語法(cramped syntax)的例子
if(condition) doSomething();
while(condition) iterating ;
for(var i=0;i
// 2.A.1.1
// 使用空格來提升可讀性
if ( condition ) {
// 語句
}
while ( condition ) {
// 語句
}
for ( var i = 0; i // 語句
}
// 更好的做法:
var i,
length = 100;
for ( i = 0; i // 語句
}
// 或...
var i = 0,
length = 100;
for ( ; i // 語句
}
var prop;
for ( prop in object ) {
// 語句
}
if ( true ) {
// 語句
} else {
// 語句
}
var array = [],
object = {};
// 在一個作用域(函數)內只使用一個`var` 有助於提升可讀性
// 並且讓你的聲明列表變得有條不紊(還幫你省了幾次鍵盤敲擊)
var foo = "";
var bar = "";
var qux;
var foo = "",
bar = "",
quux;
var // 對這些變數的註解
foo = "",
bar = "",
quux;
// `var` 語句必須總是在各自作用域(函數)頂部
// 同樣適應於來自 ECMAScript 6 的常數
function foo() {
var bar = "",
qux;
}
function foo() {
var bar = "",
qux;
}
// 2.B.2.1
// 命名函數宣告
function foo( arg1, argN ) {
// 使用方法
foo( arg1, argN );
// 命名函數宣告
function square( number ) {
return number * number;
}
square( 10 );
function square( number, callback ) {
callback( number * number );
}
// 回呼內容
});
// 函數表達式
var square = function( number ) {
// 傳回有價值的、相關的內容
return number * number;
};
// 這種首選形式有附加的函數讓其可以呼叫自身
// 並且在堆疊中有標識符
var factorial = function factorial ( number ) {
if ( number return 1;
}
};
// 建構子宣告
function FooBar( options ) {
}
var fooBar = new FooBar({ a: "alpha" });
// { a: "alpha" }
// 函數接受 `array` 作為參數,沒有空格
foo([ "alpha", "beta" ]);
// 2.C.1.2
// 函數接受`object` 作為參數,沒有空格
foo({
a: "alpha",
b: "beta"
});
// 函數接受 `string` 字面量為參數,沒有空格
foo("bar");
// 分組用的小括號內部,沒有空格
if ( !("foo" in obj) ) {
}
在 2.A-2.C 節,留白作為一個推薦方式被提出,基於單純的、更高的目的:統一。值得注意的是格式化偏好,像是「內部留白」必須是可選的,但在整個專案的原始碼中必須只存在一種。
// 2.D.1.1
// 語句
}
// 語句
}
無論你選擇單引號還是雙引號都無所謂,在 JavaScript 中它們在解析上沒有區別。而絕對需要強制的是一致性。 永遠不要在同一個項目中混用兩種引號,選擇一種,並保持一致。
F. 行末和空白行
留白會破壞差異並使用變更不可讀。考慮包括一個預先提交的 hook 自動刪除行末和空白行中的空格。
三、型別偵測 (源自 jQuery Core Style Guidelines)
A. 直接型別(實際型,Actual Types)
複製代碼
給定的HTML:
// 3.B.1.1
// `foo` 已被賦予值 `0`,型別為 `number`
// typeof foo;
// "number"
// 在後續的程式碼中,你需要更新 `foo`,賦予在 input 元素中得到的新值
foo = document.getElementById("foo-input").value;
// 如果你現在測試 `typeof foo`, 結果將是 `string`
if ( foo === 1 ) {
importantTask();
}
// `importantTask()` 將永遠不會被執行,即使 `foo` 有一個值 "1"
// 3.B.1.2
// 你可以巧妙地使用 / - 一元運算子強制轉換類型以解決問題:
foo = document.getElementById("foo-input").value;
// typeof foo;
if ( foo === 1 ) {
importantTask();
}
var number = 1,
string = "1",
number;
number "";
string;
string;
string ;
string;
bool;
bool;
bool "";
// "false"
var number = 1,
string = "1",
string === number;
string === number "";
string === number;
bool === number;
bool === number;
bool === string;
bool === !!string;
// true
var array = [ "a", "b", "c" ];
!!~array.indexOf("a");
!!~array.indexOf("b");
!!~array.indexOf("c");
!!~array.indexOf("d");
// 值得注意的是上述都是 "不必要的聰明"
// 採用明確的方案來比較傳回的值
if ( array.indexOf( "a" ) >= 0 ) {
// ...
}
var num = 2.5;
parseInt( num, 10 );
// 等價於...
~~num;
num >> 0;
num >>> 0;
// 結果都是 2
// 時時牢記心底, 負值將被區別對待...
var neg = -2.5;
parseInt( neg, 10 );
// 等價於...
~~neg;
neg >> 0;
// 結果都是 -2
neg >>> 0;
四、對比運算
程式碼如下:
// ...判斷真偽, 請使用這種:
if ( array.length ) ...
// 4.1.2
// 當只是判斷一個 array 是否為空,相對於使用這個:
if ( array.length === 0 ) ...
// ...判斷真偽, 請使用這種:
if ( !array.length ) ...
// 4.1.3
// 當只是判斷一個 string 是否為空,相對於使用這個:
if ( string !== "" ) ...
// ...判斷真偽, 請使用這種:
if ( string ) ...
// 4.1.4
// 當只是判斷一個 string 是為空,相對於使用這個:
if ( string === "" ) ...
// ...判斷真偽, 請使用這種:
if ( !string ) ...
// 4.1.5
// 當只是判斷一個引用是為真,相對於使用這個:
if ( foo === true ) ...
// ...判斷只需像你所想,享受內建功能的好處:
if ( foo ) ...
// 4.1.6
// 當只是判斷一個引用是為假,相對於使用這個:
if ( foo === false ) ...
// ...使用嘆號將其轉換為真
if ( !foo ) ...
// ...要注意的是:這個將會匹配0, "", null, undefined, NaN
// 如果你_必須_ 是布林類型的false,請這樣用:
if ( foo === false ) ...
// 4.1.7
// 如果想要計算一個引用可能是null 或undefined,但並不是false, "" 或0,
// 相對於使用這個:
if ( foo === null || foo === undefined ) ...
// ...享受 == 類型強制轉換的好處,像這樣:
if ( foo == null ) ...
// 謹記,使用== 將會令`null` 符合`null` 和`undefined`
// 但不是`false`,"" 或0
null == undefined
總是判斷最好、最精確的數值,上述是指南而非教條。
// 4.2.1
// 類型轉換和對比運算說明// 首次 `===`,`==` 次之 (除非需要鬆散類型的對比)
// `===` 總是不做型別轉換,這表示:
"1" === 1;
// false
// `==` 會轉換類型,這表示:
"1" == 1;
// true
// 4.2.2
// 布爾, 真 & 偽
// 布林:
true, false
// 真:
"foo", 1
"", 0, null, undefined, NaN, void 0
代碼如下:
// 5.1.1
(function( global ) {
var Module = (function() {
var data = "secret";
return {
// 這是一個布林值
bool: true,
一個數組
array: [ 1, 2, 3, 4 ],
// 一個物件
object: {
object: {
object: {
getData: function() {
// 得到`data` 的值
return data;
}, return ( data = value );
}
};
})();
// 其他一些將會出現在這裡
// 把你的模組變成全域物件
global.Module = Module;
})( this );
// 5.2.1
// 一個實用的建構子
(function( global ) {
function Ctor( foo ) {
this.foo = foo;
return this;
}
Ctor.prototype.getFoo = function() {
return this.foo;
};
Ctor.prototype.setFoo = function( val ) {
return ( this.foo = val );
};
// 不使用 `new` 來呼叫建構函數,你可能會這樣做:
var ctor = function( foo ) {
return new Ctor( foo );
};
;
;
;
六、命名
A. 你不是一個人肉 編譯器/壓縮器,所以試著去變身為其一。
下面的代碼是一個極糟命名的典範:
function q(s) {
return document.querySelectorAll(s);
}
var i,a=[],els=q("#foo");
for( i=0;i
毫無疑問,你寫過這樣的程式碼- 希望從今天它不再出現。
這裡有一個相同邏輯的程式碼,但擁有更健壯、貼切的命名(和一個可讀的結構):
// 6.A.2.1
// 改善過命名的範例程式碼
function query( selector ) {
return document.querySelectorAll( selector );
}
elements = [],
matches = query("#foo"),
length = matches.length;
// 命名字串
// 6.A.3.1
`dog` 是一個 string
// 6.A.3.2
`['dogs']` 是一個包含 `dog 字串的 array
// 6.A.3.3
camlCase; function 和 var 聲明
// 6.A.3.4
PascalCase; 建構函數
// 6.A.3.5
rDesc = //;
// 6.A.3.6
// 來自 Google Closure Library Style Guide
functionNamesLikeThis;
methodNamesLikeThisSYTSS453; this
// 6.B.1
function Device ( opts ) {
this.value = null;
stream.read( opts.path, function( data ) {
// 使用 stream 傳回 data 最新的值,更新實例的值
this.value = data;
}.bind(this) );
setInterval(function() {
this.emit("event");
}
程式碼如下:
// 6.B.2
// 6.B.2// 例:lodash/underscore,_.bind()
function Device( opts ) {
this.value = null;
stream.read( opts.path, _.bind(function( data ) {
this.value = data;
setInterval(_.bind(function() {
this.emit("event");
}, this), opts.freq || 100 );
}// 範例:jQuery.proxy
function Device( opts ) {
this.value = null;
stream.read( opts.path, jQuery.proxy(function( data ) {
this.value = data;
setInterval( jQuery.proxy(function() {
this.emit("event");
}, this), opts.freq || 100 );
}// 例:dojo.hitch
function Device( opts ) {
this.value = null;
stream.read( opts.path, dojo.hitch( this, function( data ) {
this.value = data;
}) );
// 6.B.3
function Device( opts ) {
var self = this;this.value = null;
stream.read( opts.path, function( data ) {
self.value = data;
});
setInterval(function() {
self.emit("event");
C. 使用 thisArg
程式碼如下:
// 6.C.1var obj;
obj = { f: "foo", b: "bar", q: "qux" };
Object.keys( obj ).forEach(function( key ) {
// |this| 現在是 `obj`
console.log( this[ key ] );
}, obj ); //
// 印出來...
// "foo"// "bar"// "qux"
thisArg 在Array.prototype.every、 Array.prototype.forEach、 Array.prototype .some、 Array.prototype.map、 Array.prototype.filter 中都可以使用。
七、Misc
這個部分將要說明的想法和理念都並非教條。相反更鼓勵對現存實踐保持好奇,以嘗試提供完成一般 JavaScript 程式設計任務的更好方案。
程式碼如下:
// 7.A.1.1
// switch 語句範例
switch( foo ) {
case "alpha":
alpha();
break;
break;
case "beta":
> default:
break;
}
// 7.A.1.2
// 一個可支援組合、重用的方法是使用一個物件來儲存 “cases”,
// 使用一個 function 來做委派:
var cases, delegator;
// 回傳值僅供說明用
cases = {
alpha: function() {
// 語句
// 一個回傳值 .length ];
},
beta: function() {
// 語句
// 一個回傳值
> return [ "Beta l. ,
_default: function() {
// 一個回傳值
return [ "Default", arguments.length ];
var args, key, delegate;
args = [].slice.call( arguments );
key = args.shift();
delegate = cases._default;
// 從物件中委派運算方法
if ( cases.hasOwnProperty( key ) ) {
}
// arg 的作用域可以設定成特定值,
// 這種情況下,|null| 就可以了
};
// 使用 7.A.1.2 中的 API:
// [ "Alpha", 5 ]
// 當然 `case` key 的值可以輕鬆地換成任意值
var caseKey, someUserInput;
someUserInput = 9;
if ( someUserInput > 10 ) {
caseKey = "alpha";
} else {
}
// 或...
caseKey = someUserInput > 10 ? "alpha" : "beta";
// 然後...
// [ "Beta", 1 ]
// 當然還可以這樣搞...
B. 提前回傳值提升程式碼的可讀性且沒有太多效能上的差異
if ( foo ) {
ret = "foo";
} else {
ret = "quux";
}
// 好:
function returnEarly( foo ) {
if ( foo ) {
return "foo"; }
return "quux";
}
最基本的原則是:
不要做任何蠢事,事情總是會變好的。
為了加強這個觀念,請觀看這個示範:
「一切都被允許: 原生擴充」 by Andrew Dupont (JSConf2011, Portland, Oregon)
http://blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542
九、註單行註解放於程式碼上方為首選
多行也可以行末註解應被避免!
JSDoc 的方式也不錯,但需要比較多的時間
無論是什麼語言程式維護者(或團隊)規定使用何種語言,程式都應只用同一種語言書寫。
附錄
前置逗號(Comma First)
所有使用此文件作為基本風格指南的項目都不允許前置逗號的程式碼格式,除非明確指定或作者要求。