Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
492 lines (347 sloc) 11.7 KB

CoffeeScript 风格指南

这份文档包含了一系列CoffeeScript编程语言的最佳实战。

希望能有更多的人一起做贡献,不断丰富文档。

请知晓,这是一个仍然在编写的项目:一些内容可能不符合社区习惯,这些内容会根据实际情况修改或删除。

灵感

这份文档的灵感来自与许多风格指南,例如:

目录

## 代码布局 ### 制表符还是空格?

建议只使用空格键,每 2个空格 作为一个缩进级别,不要混用制表符和空格键。

### 最大行长

每行最多包含79个字符

### 空行

顶层函数和类声明语句后应空一行。

在类中的函数声明语句后也应当空一行。

在方法或函数内头尾各空一行可以提高代码的可读性。(例如,为了勾勒出代码逻辑部分)

### 尾随空白

任何一行都不应该包含有尾随空白。

### 可选的逗号

在对象属性或者元素定义,以及需要多行定义的数组中,尽量避免使用逗号。

# 正确
foo = [
  'some'
  'string'
  'values'
]
bar:
  label: 'test'
  value: 87

# 错误
foo = [
  'some',
  'string',
  'values'
]
bar:
  label: 'test',
  value: 87
### 编码

建议使用UTF-8进行源文件编码

## 模块引入

当采用模块化系统(例如CommonJs 模块 AMD 等)时,require语句应当独立成行。

require 'lib/setup'
Backbone = require 'backbone'

模块引入语句应当更具如下顺序进行分组:

  1. 标准库 (如果引入了标准库)
  2. 第三方库
  3. 本地库 (如果引入了本地库)
## 声明、表达式中的空格

在下述情况中,避免使用额外的空格

  • 在括号中

       ($ 'body') # 正确
       ( $ 'body' ) # 错误
  • 逗号前

       console.log x, y # 正确
       console.log x , y # 错误

额外建议:

  • 在下列操作符两边加上 一个空格

    • 赋值: =

      • _定义默认参数的时候也是这样

        test: (param = null) -> # 正确
        test: (param=null) -> # 错误
    • 运算操作: +=, -=, etc.

    • 比较操作: ==, <, >, <=, >=, unless, etc.

    • 四则运算: +, -, *, /, etc.

    • (不要用多余一个的空格去包围操作符)

         # 正确
         x = 1
         y = 1
         fooBar = 3
      
         # 错误
         x      = 1
         y      = 1
         fooBar = 3
## 注释

如果修改的代码有相关注释,也应当对注释进行更新,总之,注释应当和代码功能保持一致。

注释的第一个单词应当大写,除非第一个单词必须使用小写字母。

如果注释内容很少,结尾的内容可以省略

### 块级注释

块级注释应当写在所要解释的代码块之前。

每行块级注释都应当用#和一个空格作为开头,并且应当和所要注释的代码保持同样的缩进。

块级注释由仅包含#的那行行进行分段。

  # This is a block comment. Note that if this were a real block
  # comment, we would actually be describing the proceeding code.
  #
  # This is the second paragraph of the same block comment. Note
  # that this paragraph was separated from the previous paragraph
  # by a line containing a single comment character.

  init()
  start()
  stop()
### 行内注释

行内注释应当紧跟所要注释的代码语句之后,如果行内注释内容特别短,可以直接和语句写在一行内(通常在注释前多加一个空格)。

所有的行内注释都应当由一个#号和一个空格作为开头。

应当尽量限制行内注释的数量,因为他们看起来很像一行代码。

不要去描述那些显而易见的代码运行结果。

  # No
  x = x + 1 # Increment x

而应当着重描述代码的运行逻辑。

  # Yes
  x = x + 1 # Compensate for border
## 命名

使用 camelCase (驼峰式)的格式去命名所有的变量、方法和对象属性。

使用 CamelCase 去命名所有的类 (这种风格参照了PascalCase, CamelCaps, 以及 CapWords, 在other alternatives中有描述.)

(CoffeeScript的官方命名方式是camelcase,这是为了尽可能的和javascript协作,详情可以参照这里。)

对于常量,命名需要使用大写字母和下划线。

CONSTANT_LIKE_THIS

对于私有的函数和变量在开头应当用下划线标注。

_privateMethod: ->
## 函数

(这些规则同样适用于类的定义)

当声明的函数中有参数列表,每个参数之间应当也有一个空格进行间隔。

foo = (arg1, arg2) -> # Yes
foo = (arg1, arg2)-> # No

当声明的函数不需要参数,不要插入额外的括号。

bar = -> # Yes
bar = () -> # No

如果需要进行多次链式调用,不要将他们写在一起,每次调用都应当独立成行,并用两个空格进行缩进,用.开头。

[1..3]
  .map((x) -> x * x)
  .concat([10..12])
  .filter((x) -> x < 11)
  .reduce((x, y) -> x + y)

当调用方法时,为了保持代码的可读性,尽可能的省略不必要的括号。记住一点,"可读性"是主观的,下面的例子,是社区认为处理省略括号比较恰当的方式。

baz 12

brush.ellipse x: 10, y: 20 # Braces can also be omitted or included for readability

foo(4).bar(8)

obj.value(10, 20) / obj.value(20, 10)

print inspect value

new Tag(new Value(a, b), new Arg(c))

有时候,你会发现括号被用来进行函数分组(而不是进行参数分组)。例如使用这种风格。

($ '#selektor').addClass 'klass'

(foo 4).bar 8

这和这种风格不一致:

$('#selektor').addClass 'klass'

foo(4).bar 8

当使用链式调用函数时,一些这种风格的实现更倾向于只在第一次调用的时候使用函数分组。

($ '#selektor').addClass('klass').hide() # Initial call only
(($ '#selektor').addClass 'klass').hide() # All calls

这里不推荐使用函数分组,但是,如果某个项目使用函数分组,也请和项目保持一致,使用函数分组的风格

## 字符串

在字符串中插入变量而不要手动拼接字符串:

"this is an #{adjective} string" # 正确
"this is an " + adjective + " string" # 错误

除非需求是插入双引号,尽可能的使用单引号 ('') 而不是双引号 ("") 字符串,.

## 条件

使用 unless 而不是 if 处理期望条件不成立的情况.

但是不要使用 unless...else, 而使用 if...else:

  # 正确
  if true
    ...
  else
    ...

  # 错误
  unless false
    ...
  else
    ...

多行的 if/else 应当独立成行并使用缩进:

  # 正确
  if true
    ...
  else
    ...

  # 错误
  if true then ...
  else ...
## 循环

尽可能的使用comprehensions进行遍历:

  # 正确
  result = (item.name for item in array)

  # 错误
  results = []
  for item in array
    results.push item.name

处理筛选:

result = (item for item in array when item.name is "test")

遍历键值对:

object = one: 1, two: 2
alert("#{key} = #{value}") for key, value of object
## 扩展类

不要修改原生对象

比如不要在Array.prototype上实现Array#forEach方法

## 异常

不要总是忽略异常.

## 注释

注释应该用于解释代码执行的特定功能

将注释写在所要描述的代码片段之前

注释关键字和注释正文之间应当由一个冒号和一个空格分隔

  # FIXME: The client's current state should *not* affect payload processing.
  resetClientState()
  processPayload()

如果注释有很多行,后续内容用两个空格进行缩进。

  # 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文件中。

## 其他

and优于&&.

or优于||.

is优于==.

not优于!.

尽可能使用or=:

temp or= {} # 正确
temp = temp || {} # 错误

使用(::)访问对象的prototype:

Array::slice # 正确
Array.prototype.slice # 错误

@property优于this.property

return @property # 正确
return this.property # 错误

但是要避免单独使用 @:

return this # 正确
return @ # 错误

除非显式return有助于可读性,否则应尽量避免显式return

当函数的参数数量不固定时,使用splats (...) :

console.log args... # 正确

(a, b, c, rest...) -> # 正确