├── README.md
└── coffeelint.json
/README.md:
--------------------------------------------------------------------------------
1 | # CoffeeScript 编码风格指南
2 |
3 |
4 |
5 | 这份指南阐述了一些 [CoffeeScript][coffeescript] 的最佳实践和编码惯例。
6 |
7 | 这份指南是社群驱动的,非常鼓励大家来贡献内容。
8 |
9 | 请注意这还是一份正在完善的指南:仍有很多地方可以改进,有些已制定的准则也不一定是社区惯用的(基于此,在适当的情况下,这些有待斟酌的准则将有可能被修改或删除。)
10 |
11 | ## 灵感
12 |
13 | 本指南中的很多细节受到了几份现有的风格指南和其他资源的启发。特别是:
14 |
15 | - [PEP-8][pep8]: Style Guide for Python Code
16 | - Bozhidar Batsov's [Ruby Style Guide][ruby-style-guide]
17 | - [Google's JavaScript Style Guide][google-js-styleguide]
18 | - [Common CoffeeScript Idioms][common-coffeescript-idioms]
19 | - Thomas Reynolds' [CoffeeScript-specific Style Guide][coffeescript-specific-style-guide]
20 | - Jeremy Ashkenas' [code review][spine-js-code-review] of [Spine][spine-js]
21 | - The [CoffeeScript FAQ][coffeescript-faq]
22 |
23 | ## 目录
24 |
25 | * [CoffeeScript 风格指南](#guide)
26 | * [代码布局(Code Layout)](#code_layout)
27 | * [Tab 还是 空格?(Tabs or Spaces?)](#tabs_or_spaces)
28 | * [最大行宽(Maximum Line Length)](#maximum_line_length)
29 | * [空行(Blank Lines)](#blank_lines)
30 | * [结尾空白(Trailing Whitespace)](#trailing_whitespace)
31 | * [可选的逗号(Optional Commas)](#optional_commas)
32 | * [编码(Encoding)](#encoding)
33 | * [模块导入(Module Imports)](#module_imports)
34 | * [表达式和语句中的空白(Whitespace in Expressions and Statements)](#whitespace)
35 | * [注释(Comments)](#comments)
36 | * [块注释(Block Comments)](#block_comments)
37 | * [行内注释(Inline Comments)](#inline_comments)
38 | * [命名规范(Naming Conventions)](#naming_conventions)
39 | * [函数(Functions)](#functions)
40 | * [字符串(Strings)](#strings)
41 | * [条件判断(Conditionals)](#conditionals)
42 | * [循环和列表解析(Looping and Comprehensions)](#looping_and_comprehensions)
43 | * [扩展本地对象(Extending Native Objects)](#extending_native_objects)
44 | * [异常(Exceptions)](#exceptions)
45 | * [注解(Annotations)](#annotations)
46 | * [其他(Miscellaneous)](#miscellaneous)
47 |
48 |
49 | ## 代码布局(Code Layout)
50 |
51 |
52 | ### Tab 还是 空格?(Tabs or Spaces?)
53 |
54 | 只用 **空格**,每级缩进均为 **2 个空格**。切勿混用 Tab 和空格。
55 |
56 |
57 | ### 最大行宽(Maximum Line Length)
58 |
59 | 限制每行最多 79 个字符。
60 |
61 |
62 | ### 空行(Blank Lines)
63 |
64 | 顶级函数和类的定义用一个空行分开。
65 |
66 | 类内部的函数定义也用一个空行分开。
67 |
68 | 对于每个函数体内,只在为了提高可读性的情况下才使用一个空行(例如:为了达到划分逻辑的目的)。
69 |
70 |
71 | ### 结尾空白(Trailing Whitespace)
72 |
73 | 不要在任何一行保留行尾空白。
74 |
75 |
76 | ### 可选的逗号(Optional Commas)
77 |
78 | 当对象(或数组)的属性(或元素)作为单独一行列出时,避免在换行符前使用逗号。如下:
79 |
80 | ```coffeescript
81 | # 好
82 | foo = [
83 | 'some'
84 | 'string'
85 | 'values'
86 | ]
87 | bar:
88 | label: 'test'
89 | value: 87
90 |
91 | # 差
92 | foo = [
93 | 'some',
94 | 'string',
95 | 'values'
96 | ]
97 | bar:
98 | label: 'test',
99 | value: 87
100 | ```
101 |
102 |
103 | ### 编码(Encoding)
104 |
105 | UTF-8 是首选的源文件编码。
106 |
107 |
108 | ## 模块导入(Module Imports)
109 |
110 | 如果需要导入模块 (CommonJS 模块,AMD,等等.), `require` 语句应该单独作为一行。如下:
111 |
112 | ```coffeescript
113 | require 'lib/setup'
114 | Backbone = require 'backbone'
115 | ```
116 |
117 | 这些语句应该按以下顺序去分组:
118 |
119 | 1. 标准库的导入 _(如果标准库存在)_
120 | 2. 第三方库的导入
121 | 3. 本地导入 _(导入这个应用程序的或库的具体依赖)_
122 |
123 |
124 | ## 表达式和语句中的空白(Whitespace in Expressions and Statements)
125 |
126 | 下列情况应该避免多余的空格:
127 |
128 | - 紧贴着圆括号、方括号和大括号内部
129 |
130 | ```coffeescript
131 | ($ 'body') # 好
132 | ( $ 'body' ) # 差
133 | ```
134 |
135 | - 紧贴在逗号前
136 |
137 | ```coffeescript
138 | console.log x, y # 好
139 | console.log x , y # 差
140 | ```
141 |
142 | 额外建议:
143 |
144 | - 在下列二元操作符的左右两边都保留 **一个空格**
145 |
146 | - 赋值运算符: `=`
147 |
148 | - _注意这同样适用于函数定义中的默认参数_
149 |
150 | ```coffeescript
151 | test: (param = null) -> # 好
152 | test: (param=null) -> # 差
153 | ```
154 |
155 | - 自增运算符: `+=`, `-=`, 等等。
156 | - 比较运算符: `==`, `<`, `>`, `<=`, `>=`, `unless`, 等等。
157 | - 算术运算符: `+`, `-`, `*`, `/`, 等等。
158 |
159 | - _(这些操作符两边的空格不要多于一个)_
160 |
161 | ```coffeescript
162 | # 好
163 | x = 1
164 | y = 1
165 | fooBar = 3
166 |
167 | # 差
168 | x = 1
169 | y = 1
170 | fooBar = 3
171 | ```
172 |
173 |
174 | ## 注释(Comments)
175 |
176 | 如果你修改了一段已有注释说明的代码,则也要更新它对应的注释。(理想状态是,重构这段代码直到它不需要注释说明,然后再把之前的注释全删掉。)
177 |
178 | 注释的首字母要大写,除非第一个单词是以小写字母开头的标识符。
179 |
180 | 如果注释很短,可以省略末尾的句号。
181 |
182 |
183 | ### 块注释(Block Comments)
184 |
185 | 注释块通常应用于尾随其后的一段代码。
186 |
187 | 每一行注释都以 `#` 加一个空格开头,而且和被注释的代码有相同的缩进层次。
188 |
189 | 注释块内的段落以仅含单个 `#` 的行分割。
190 |
191 | ```coffeescript
192 | # 这是一个块注释。请注意假如这是一段块注释,
193 | # 则它描述的就应该是接下来的这段代码。
194 | #
195 | # 这是块注释的第二段。
196 | # 请注意这段是由上一行带有 # 号的空行分开的。(P.S. 最好用英文写注释)
197 |
198 | init()
199 | start()
200 | stop()
201 | ```
202 |
203 |
204 | ### 行内注释(Inline Comments)
205 |
206 | 行内注释紧贴在被描述的代码的上一行,如果行内注释足够短,则可以处在同一行行尾(由一个空格隔开)。
207 |
208 | 所有行内注释都以 `#` 加一个空格开头。
209 |
210 | 应该限制行内注释的使用,因为它们的存在通常是一个代码异味的标志。
211 |
212 | 不要给显而易见的情况作行内注释:
213 |
214 | ```coffeescript
215 | # 差
216 | x = x + 1 # x 自增
217 | ```
218 |
219 | 然而,行内注释在某些情况下是有用的:
220 |
221 | ```coffeescript
222 | # 好
223 | x = x + 1 # 边界补足
224 | ```
225 |
226 |
227 | ## 命名规范(Naming Conventions)
228 |
229 | 使用 `小驼峰命名法` (第一个词的首字母小写,后面每个词的首字母大写)来命名所有的变量、方法和对象属性。
230 |
231 | 使用 `大驼峰命名法` (第一个词的首字母,以及后面每个词的首字母都大写)来命名所有的类 _(在[其他类似的命名法][camel-case-variations]中,这种风格通常也被称为 `帕斯卡命名法(PascalCase)`、 `大写驼峰命名法(CamelCaps)` 或 `首字母大写命名法(CapWords)`。)_
232 |
233 | _(CoffeeScript **官方** 约定是用驼峰命名法,因为这可以简化与 JavaScript 的相互转化,想了解更多,请看[这里][coffeescript-issue-425].)_
234 |
235 | 对于常量,单词全部大写,用下划线隔开即可:
236 |
237 | ```coffeescript
238 | CONSTANT_LIKE_THIS
239 | ```
240 |
241 | 私有函数和私有变量都应该在前面加一个下划线:
242 |
243 | ```coffeescript
244 | _privateMethod: ->
245 | ```
246 |
247 |
248 | ## 函数(Functions)
249 |
250 | _(以下这些准则同样适用于类中的方法。)_
251 |
252 | 当声明一个带参函数时,应在参数列表的右圆括号后空出一个空格:
253 |
254 | ```coffeescript
255 | foo = (arg1, arg2) -> # 好
256 | foo = (arg1, arg2)-> # 差
257 | ```
258 |
259 | 无参函数不要用圆括号:
260 |
261 | ```coffeescript
262 | bar = -> # 好
263 | bar = () -> # 差
264 | ```
265 |
266 | 当函数链式调用,却在一行放不下时,则把每个函数调用都另起一行,且都缩进一级(即在 `.` 前加两个空格)。
267 |
268 | ```coffeescript
269 | [1..3]
270 | .map((x) -> x * x)
271 | .concat([10..12])
272 | .filter((x) -> x < 11)
273 | .reduce((x, y) -> x + y)
274 | ```
275 |
276 | 当调用函数时,我们应该为了提高可读性而去掉圆括号。请记住,「可读性」是我们主观臆断的。只有类似下面几个例子的情况才被社区认为是最佳的:
277 |
278 | ```coffeescript
279 | baz 12
280 |
281 | brush.ellipse x: 10, y: 20 # 大括号在适当的时候也可以去掉
282 |
283 | foo(4).bar(8)
284 |
285 | obj.value(10, 20) / obj.value(20, 10)
286 |
287 | print inspect value
288 |
289 | new Tag(new Value(a, b), new Arg(c))
290 | ```
291 |
292 | 有时候你会发现圆括号用来包裹的是函数体(而不是函数的参数)。请看下面的例子(以下简称为「函数体风格」):
293 |
294 | ```coffeescript
295 | ($ '#selektor').addClass 'klass'
296 |
297 | (foo 4).bar 8
298 | ```
299 |
300 | 这段代码会编译为:
301 |
302 | ```coffeescript
303 | $('#selektor').addClass 'klass'
304 |
305 | foo(4).bar 8
306 | ```
307 |
308 | 一些习惯链式调用的人会巧用「函数体风格」进行单独初始化:
309 |
310 | ```coffeescript
311 | ($ '#selektor').addClass('klass').hide() # 单独初始化调用
312 | (($ '#selektor').addClass 'klass').hide() # 全部调用
313 | ```
314 |
315 | 「函数体风格」并不得到推荐。但是, **当它适应一些特殊的项目需求时,还是得用它。**
316 |
317 |
318 | ## 字符串(Strings)
319 |
320 | 用字符串插值代替字符串连接符:
321 |
322 | ```coffeescript
323 | 'this is an #{adjective} string' # 好
324 | 'this is an ' + adjective + ' string' # 差
325 | ```
326 |
327 | 最好用单引号 (`''`) 而不是双引号 (`""`) 。除非是插入到另一段现有的字符串中(类似字符串插值)。
328 |
329 |
330 | ## 条件判断(Conditionals)
331 |
332 | 用 `unless` 来代替 `if` 的否定情况。
333 |
334 | 不要用 `unless...else`, 而用 `if...else`:
335 |
336 | ```coffeescript
337 | # 好
338 | if true
339 | ...
340 | else
341 | ...
342 |
343 | # 差
344 | unless false
345 | ...
346 | else
347 | ...
348 | ```
349 |
350 | 多行的 if/else 语句应该缩进:
351 |
352 | ```coffeescript
353 | # 好
354 | if true
355 | ...
356 | else
357 | ...
358 |
359 | # 差
360 | if true then ...
361 | else ...
362 | ```
363 |
364 |
365 | ## 循环和列表解析(Looping and Comprehensions)
366 |
367 | 尽可能的使用列表解析:
368 |
369 | ```coffeescript
370 | # 好
371 | result = (item.name for item in array)
372 |
373 | # 差
374 | results = []
375 | for item in array
376 | results.push item.name
377 | ```
378 |
379 | 还可以过滤结果:
380 |
381 | ```coffeescript
382 | result = (item for item in array when item.name is "test")
383 | ```
384 |
385 | 遍历对象的键值:
386 |
387 | ```coffeescript
388 | object = one: 1, two: 2
389 | alert("#{key} = #{value}") for key, value of object
390 | ```
391 |
392 |
393 | ## 扩展本地对象(Extending Native Objects)
394 |
395 | 不要修改本地对象。
396 |
397 | 比如,不要给 `Array.prototype` 引入 `Array#forEach` 。
398 |
399 |
400 | ## 异常(Exceptions)
401 |
402 | 不要抑制异常抛出。
403 |
404 |
405 | ## 注解(Annotations)
406 |
407 | 必要的时候应该写注解,来指明接下来的代码块具体将干什么。
408 |
409 | 注解应紧贴在被描述代码的上一行。
410 |
411 | 注解关键字后面应该跟一个冒号加一个空格,加一个描述性的注释。
412 |
413 | ```coffeescript
414 | # FIXME: The client's current state should *not* affect payload processing.
415 | resetClientState()
416 | processPayload()
417 | ```
418 |
419 | 如果注解不止一行,则下一行缩进两个空格。
420 |
421 | ```coffeescript
422 | # TODO: Ensure that the value returned by this call falls within a certain
423 | # range, or throw an exception.
424 | analyze()
425 | ```
426 |
427 | 注解有以下几类:
428 |
429 | - `TODO`: 描述缺失的功能,以便日后加入
430 | - `FIXME`: 描述需要修复的代码
431 | - `OPTIMIZE`: 描述性能低下,或难以优化的代码
432 | - `HACK`: 描述一段值得质疑(或很巧妙)的代码
433 | - `REVIEW`: 描述需要确认其编码意图是否正确的代码
434 |
435 | 如果你必须自定义一个新的注解类型,则应该把这个注解类型记录在项目的 README 里面。
436 |
437 |
438 | ## 其他(Miscellaneous)
439 |
440 | `and` 更优于 `&&`.
441 |
442 | `or` 更优于 `||`.
443 |
444 | `is` 更优于 `==`.
445 |
446 | `not` 更优于 `!`.
447 |
448 | `or=` 应在可能的情况下使用:
449 |
450 | ```coffeescript
451 | temp or= {} # 好
452 | temp = temp || {} # 差
453 | ```
454 |
455 | 最好用 (`::`) 访问对象的原型:
456 |
457 | ```coffeescript
458 | Array::slice # 好
459 | Array.prototype.slice # 差
460 | ```
461 |
462 | 最好用 `@property` 而不是 `this.property`.
463 |
464 | ```coffeescript
465 | return @property # 好
466 | return this.property # 差
467 | ```
468 |
469 | 但是,避免使用 **单独的** `@`:
470 |
471 | ```coffeescript
472 | return this # 好
473 | return @ # 差
474 | ```
475 |
476 | 没有返回值的时候避免使用 `return` ,其他情况则需要显示 return 。
477 |
478 | 当函数需要接收可变数量的参数时,使用 splats (`...`)。
479 |
480 | ```coffeescript
481 | console.log args... # 好
482 |
483 | (a, b, c, rest...) -> # 好
484 | ```
485 |
486 | [coffeescript]: http://jashkenas.github.com/coffee-script/
487 | [coffeescript-issue-425]: https://github.com/jashkenas/coffee-script/issues/425
488 | [spine-js]: http://spinejs.com/
489 | [spine-js-code-review]: https://gist.github.com/1005723
490 | [pep8]: http://www.python.org/dev/peps/pep-0008/
491 | [ruby-style-guide]: https://github.com/bbatsov/ruby-style-guide
492 | [google-js-styleguide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
493 | [common-coffeescript-idioms]: http://arcturo.github.com/library/coffeescript/04_idioms.html
494 | [coffeescript-specific-style-guide]: http://awardwinningfjords.com/2011/05/13/coffeescript-specific-style-guide.html
495 | [coffeescript-faq]: https://github.com/jashkenas/coffee-script/wiki/FAQ
496 | [camel-case-variations]: http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
497 |
498 |
--------------------------------------------------------------------------------
/coffeelint.json:
--------------------------------------------------------------------------------
1 | {
2 | "coffeescript_error": {
3 | "level": "error"
4 | },
5 | "arrow_spacing": {
6 | "name": "arrow_spacing",
7 | "level": "warn"
8 | },
9 | "no_tabs": {
10 | "name": "no_tabs",
11 | "level": "error"
12 | },
13 | "no_trailing_whitespace": {
14 | "name": "no_trailing_whitespace",
15 | "level": "warn",
16 | "allowed_in_comments": false,
17 | "allowed_in_empty_lines": true
18 | },
19 | "max_line_length": {
20 | "name": "max_line_length",
21 | "value": 80,
22 | "level": "warn",
23 | "limitComments": true
24 | },
25 | "line_endings": {
26 | "name": "line_endings",
27 | "level": "ignore",
28 | "value": "unix"
29 | },
30 | "no_trailing_semicolons": {
31 | "name": "no_trailing_semicolons",
32 | "level": "error"
33 | },
34 | "indentation": {
35 | "name": "indentation",
36 | "value": 2,
37 | "level": "error"
38 | },
39 | "camel_case_classes": {
40 | "name": "camel_case_classes",
41 | "level": "error"
42 | },
43 | "colon_assignment_spacing": {
44 | "name": "colon_assignment_spacing",
45 | "level": "warn",
46 | "spacing": {
47 | "left": 0,
48 | "right": 1
49 | }
50 | },
51 | "no_implicit_braces": {
52 | "name": "no_implicit_braces",
53 | "level": "ignore",
54 | "strict": true
55 | },
56 | "no_plusplus": {
57 | "name": "no_plusplus",
58 | "level": "ignore"
59 | },
60 | "no_throwing_strings": {
61 | "name": "no_throwing_strings",
62 | "level": "error"
63 | },
64 | "no_backticks": {
65 | "name": "no_backticks",
66 | "level": "error"
67 | },
68 | "no_implicit_parens": {
69 | "name": "no_implicit_parens",
70 | "level": "ignore"
71 | },
72 | "no_empty_param_list": {
73 | "name": "no_empty_param_list",
74 | "level": "warn"
75 | },
76 | "no_stand_alone_at": {
77 | "name": "no_stand_alone_at",
78 | "level": "ignore"
79 | },
80 | "space_operators": {
81 | "name": "space_operators",
82 | "level": "warn"
83 | },
84 | "duplicate_key": {
85 | "name": "duplicate_key",
86 | "level": "error"
87 | },
88 | "empty_constructor_needs_parens": {
89 | "name": "empty_constructor_needs_parens",
90 | "level": "ignore"
91 | },
92 | "cyclomatic_complexity": {
93 | "name": "cyclomatic_complexity",
94 | "value": 10,
95 | "level": "ignore"
96 | },
97 | "newlines_after_classes": {
98 | "name": "newlines_after_classes",
99 | "value": 3,
100 | "level": "ignore"
101 | },
102 | "no_unnecessary_fat_arrows": {
103 | "name": "no_unnecessary_fat_arrows",
104 | "level": "warn"
105 | },
106 | "missing_fat_arrows": {
107 | "name": "missing_fat_arrows",
108 | "level": "ignore"
109 | },
110 | "non_empty_constructor_needs_parens": {
111 | "name": "non_empty_constructor_needs_parens",
112 | "level": "ignore"
113 | }
114 | }
115 |
--------------------------------------------------------------------------------