這份指南闡述了一些 CoffeeScript 的最佳實踐和編碼慣例。
這份指南是社群驅動的,非常鼓勵大家來貢獻內容。
請注意這還是一份正在完善的指南:仍有很多地方可以改進,有些已制定的準則也不一定是社群慣用的(基於此,在適當的情況下,這些有待斟酌的準則將有可能被修改或刪除。)
靈感
本指南中的很多細節受到了幾份現有的風格指南和其他資源的啟發。特別是:
- PEP-8: Style Guide for Python Code
- Bozhidar Batsov’s Ruby Style Guide
- Google’s JavaScript Style Guide
- Common CoffeeScript Idioms
- Thomas Reynolds’ CoffeeScript-specific Style Guide
- Jeremy Ashkenas’ code review of Spine
- The CoffeeScript FAQ
程式碼佈局(Code Layout)
Tab 還是 空格?(Tabs or Spaces?)
只用 空格,每級縮排均為 2 個空格。切勿混用 Tab 和空格。
最大行寬(Maximum Line Length)
限制每行最多 79 個字元。
空行(Blank Lines)
- 頂級函式和類的定義用一個空行分開。
- 類內部的函式定義也用一個空行分開。
- 對於每個函式體內,只在為了提高可讀性的情況下才使用一個空行(例如:為了達到劃分邏輯的目的)。
結尾空白(Trailing Whitespace)
不要在任何一行保留行尾空白。
可選的逗號(Optional Commas)
當物件(或陣列)的屬性(或元素)作為單獨一行列出時,避免在換行符前使用逗號。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 好 foo = [ 'some' 'string' 'values' ] bar: label: 'test' value: 87 # 差 foo = [ 'some', 'string', 'values' ] bar: label: 'test', value: 87 |
編碼(Encoding)
UTF-8 是首選的原始檔編碼。
模組匯入(Module Imports)
如果需要匯入模組 (CommonJS 模組,AMD,等等.), require
語句應該單獨作為一行。如下:
1 2 |
require 'lib/setup' Backbone = require 'backbone' |
這些語句應該按以下順序去分組:
- 標準庫的匯入 (如果標準庫存在)
- 第三方庫的匯入
- 本地匯入 (匯入這個應用程式的或庫的具體依賴)
表示式和語句中的空白(Whitespace in Expressions and Statements)
下列情況應該避免多餘的空格:
- 緊貼著圓括號、方括號和大括號內部
1 2 |
($ 'body') # 好 ( $ 'body' ) # 差 |
緊貼在逗號前
1 2 |
console.log x, y # 好 console.log x , y # 差 |
額外建議:
- 在下列二元操作符的左右兩邊都保留 一個空格
- 賦值運算子:
=
注意:這同樣適用於函式定義中的預設引數
- 賦值運算子:
1 2 |
test: (param = null) -> # 好 test: (param=null) -> # 差 |
- 自增運算子:
+=
,-=
, 等等。 - 比較運算子:
==
,<
,>
,<=
,>=
,unless
, 等等。 - 算術運算子:
+
,-
,*
,/
, 等等。 - (這些操作符兩邊的空格不要多於一個)
1 2 3 4 5 6 7 8 9 |
# 好 x = 1 y = 1 fooBar = 3 # 差 x = 1 y = 1 fooBar = 3 |
註釋(Comments)
如果你修改了一段已有註釋說明的程式碼,則也要更新它對應的註釋。(理想狀態是,重構這段程式碼直到它不需要註釋說明,然後再把之前的註釋全刪掉。)
註釋的首字母要大寫,除非第一個單詞是以小寫字母開頭的識別符號。
如果註釋很短,可以省略末尾的句號。
塊註釋(Block Comments)
註釋塊通常應用於尾隨其後的一段程式碼。
每一行註釋都以 #
加一個空格開頭,而且和被註釋的程式碼有相同的縮排層次。
註釋塊內的段落以僅含單個 #
的行分割。
1 2 3 4 5 6 7 8 9 |
# 這是一個塊註釋。請注意假如這是一段塊註釋, # 則它描述的就應該是接下來的這段程式碼。 # # 這是塊註釋的第二段。 # 請注意這段是由上一行帶有 # 號的空行分開的。(P.S. 最好用英文寫註釋) init() start() stop() |
行內註釋(Inline Comments)
行內註釋緊貼在被描述的程式碼的上一行,如果行內註釋足夠短,則可以處在同一行行尾(由一個空格隔開)。
所有行內註釋都以 #
加一個空格開頭。
應該限制行內註釋的使用,因為它們的存在通常是一個程式碼異味的標誌。
不要給顯而易見的情況作行內註釋:
1 2 |
# 差 x = x + 1 # x 自增 |
然而,行內註釋在某些情況下是有用的:
1 2 |
# 好 x = x + 1 # 邊界補足 |
命名規範(Naming Conventions)
使用 小駝峰命名法
(第一個詞的首字母小寫,後面每個詞的首字母大寫)來命名所有的變數、方法和物件屬性。
使用 大駝峰命名法
(第一個詞的首字母,以及後面每個詞的首字母都大寫)來命名所有的類 (在其他類似的命名法中,這種風格通常也被稱為 帕斯卡命名法(PascalCase)
、 大寫駝峰命名法(CamelCaps)
或 首字母大寫命名法(CapWords)
。)
(CoffeeScript 官方 約定是用駝峰命名法,因為這可以簡化與 JavaScript 的相互轉化,想了解更多,請看這裡.)
對於常量,單詞全部大寫,用下劃線隔開即可:
1 |
CONSTANT_LIKE_THIS |
私有函式和私有變數都應該在前面加一個下劃線:
1 |
_privateMethod: -> |
函式(Functions)
(以下這些準則同樣適用於類中的方法。)
當宣告一個帶參函式時,應在引數列表的右圓括號後空出一個空格:
1 2 |
foo = (arg1, arg2) -> # 好 foo = (arg1, arg2)-> # 差 |
無參函式不要用圓括號:
1 2 |
bar = -> # 好 bar = () -> # 差 |
當函式鏈式呼叫,卻在一行放不下時,則把每個函式呼叫都另起一行,且都縮排一級(即在 .
前加兩個空格)。
1 2 3 4 5 |
[1..3] .map((x) -> x * x) .concat([10..12]) .filter((x) -> x < 11) .reduce((x, y) -> x + y) |
當呼叫函式時,我們應該為了提高可讀性而去掉圓括號。請記住,「可讀性」是我們主觀臆斷的。只有類似下面幾個例子的情況才被社群認為是最佳的:
1 2 3 4 5 6 7 8 9 10 11 |
baz 12 brush.ellipse x: 10, y: 20 # 大括號在適當的時候也可以去掉 foo(4).bar(8) obj.value(10, 20) / obj.value(20, 10) print inspect value new Tag(new Value(a, b), new Arg(c)) |
有時候你會發現圓括號用來包裹的是函式體(而不是函式的引數)。請看下面的例子(以下簡稱為「函式體風格」):
1 2 3 |
($ '#selektor').addClass 'klass' (foo 4).bar 8 |
這段程式碼會編譯為:
1 2 3 |
$('#selektor').addClass 'klass' foo(4).bar 8 |
一些習慣鏈式呼叫的人會巧用「函式體風格」進行單獨初始化:
1 2 |
($ '#selektor').addClass('klass').hide() # 單獨初始化呼叫 (($ '#selektor').addClass 'klass').hide() # 全部呼叫 |
「函式體風格」並不得到推薦。但是, 當它適應一些特殊的專案需求時,還是得用它。
字串(Strings)
用字串插值代替字串連線符:
1 2 |
"this is an #{adjective} string" # 好 "this is an " + adjective + " string" # 差 |
最好用單引號 (''
) 而不是雙引號 (""
) 。除非是插入到另一段現有的字串中(類似字串插值)
條件判斷(Conditionals)
用 unless
來代替 if
的否定情況。
不要用 unless...else
, 而用 if...else
:
1 2 3 4 5 6 7 8 9 10 11 |
# 好 if true ... else ... # 差 unless false ... else ... |
多行的 if/else 語句應該縮排:
1 2 3 4 5 6 7 8 9 |
# 好 if true ... else ... # 差 if true then ... else ... |
迴圈和列表解析(Looping and Comprehensions)
儘可能的使用列表解析:
1 2 3 4 5 6 7 |
# 好 result = (item.name for item in array) # 差 results = [] for item in array results.push item.name |
還可以過濾結果:
1 |
result = (item for item in array when item.name is "test") |
遍歷物件的鍵值:
1 2 |
object = one: 1, two: 2 alert("#{key} = #{value}") for key, value of object |
擴充套件本地物件(Extending Native Objects)
不要修改本地物件。
比如,不要給 Array.prototype
引入 Array#forEach
。
異常(Exceptions)
不要抑制異常丟擲。
註解(Annotations)
必要的時候應該寫註解,來指明接下來的程式碼塊具體將幹什麼。
註解應緊貼在被描述程式碼的上一行。
註解關鍵字後面應該跟一個冒號加一個空格,加一個描述性的註釋。
1 2 3 |
# FIXME: The client's current state should *not* affect payload processing. resetClientState() processPayload() |
如果註解不止一行,則下一行縮排兩個空格。
1 2 3 |
# TODO: Ensure that the value returned by this call falls within a certain # range, or throw an exception. analyze() |
註解有以下幾類:
TODO
: 描述缺失的功能,以便日後加入FIXME
: 描述需要修復的程式碼OPTIMIZE
: 描述效能低下,或難以優化的程式碼HACK
: 描述一段值得質疑(或很巧妙)的程式碼REVIEW
: 描述需要確認其編碼意圖是否正確的程式碼
如果你必須自定義一個新的註解型別,則應該把這個註解型別記錄在專案的 README 裡面。
其他(Miscellaneous)
and
更優於 &&
.
or
更優於 ||
.
is
更優於 ==
.
not
更優於 !
.
or=
應在可能的情況下使用:
1 2 |
temp or= {} # 好 temp = temp || {} # 差 |
最好用 (::
) 訪問物件的原型:
1 2 |
Array::slice # 好 Array.prototype.slice # 差 |
最好用 @property
而不是 this.property
.
1 2 |
return @property # 好 return this.property # 差 |
但是,避免使用 單獨的 @
:
1 2 |
return this # 好 return @ # 差 |
沒有返回值的時候避免使用 return
,其他情況則需要顯示 return 。
當函式需要接收可變數量的引數時,使用 splats (...
)。
1 2 3 |
console.log args... # 好 (a, b, c, rest...) -> # 好 |