Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
498 lines (347 sloc) 13.5 KB

CoffeeScript 编码风格指南

Sponsor

这份指南阐述了一些 CoffeeScript 的最佳实践和编码惯例。

这份指南是社群驱动的,非常鼓励大家来贡献内容。

请注意这还是一份正在完善的指南:仍有很多地方可以改进,有些已制定的准则也不一定是社区惯用的(基于此,在适当的情况下,这些有待斟酌的准则将有可能被修改或删除。)

灵感

本指南中的很多细节受到了几份现有的风格指南和其他资源的启发。特别是:

目录

## 代码布局(Code Layout) ### Tab 还是 空格?(Tabs or Spaces?)

只用 空格,每级缩进均为 2 个空格。切勿混用 Tab 和空格。

### 最大行宽(Maximum Line Length)

限制每行最多 79 个字符。

### 空行(Blank Lines)

顶级函数和类的定义用一个空行分开。

类内部的函数定义也用一个空行分开。

对于每个函数体内,只在为了提高可读性的情况下才使用一个空行(例如:为了达到划分逻辑的目的)。

### 结尾空白(Trailing Whitespace)

不要在任何一行保留行尾空白。

### 可选的逗号(Optional Commas)

当对象(或数组)的属性(或元素)作为单独一行列出时,避免在换行符前使用逗号。如下:

#
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 语句应该单独作为一行。如下:

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

这些语句应该按以下顺序去分组:

  1. 标准库的导入 (如果标准库存在)
  2. 第三方库的导入
  3. 本地导入 (导入这个应用程序的或库的具体依赖)
## 表达式和语句中的空白(Whitespace in Expressions and Statements)

下列情况应该避免多余的空格:

  • 紧贴着圆括号、方括号和大括号内部

       ($ 'body') #
       ( $ 'body' ) #
  • 紧贴在逗号前

       console.log x, y #
       console.log x , y #

额外建议:

  • 在下列二元操作符的左右两边都保留 一个空格

    • 赋值运算符: =

      • 注意这同样适用于函数定义中的默认参数

        test: (param = null) -> #
        test: (param=null) -> #
    • 自增运算符: +=, -=, 等等。

    • 比较运算符: ==, <, >, <=, >=, unless, 等等。

    • 算术运算符: +, -, *, /, 等等。

    • (这些操作符两边的空格不要多于一个)

         #
         x = 1
         y = 1
         fooBar = 3
      
         #
         x      = 1
         y      = 1
         fooBar = 3
## 注释(Comments)

如果你修改了一段已有注释说明的代码,则也要更新它对应的注释。(理想状态是,重构这段代码直到它不需要注释说明,然后再把之前的注释全删掉。)

注释的首字母要大写,除非第一个单词是以小写字母开头的标识符。

如果注释很短,可以省略末尾的句号。

### 块注释(Block Comments)

注释块通常应用于尾随其后的一段代码。

每一行注释都以 # 加一个空格开头,而且和被注释的代码有相同的缩进层次。

注释块内的段落以仅含单个 # 的行分割。

  # 这是一个块注释。请注意假如这是一段块注释,
  # 则它描述的就应该是接下来的这段代码。
  #
  # 这是块注释的第二段。
  # 请注意这段是由上一行带有 # 号的空行分开的。(P.S. 最好用英文写注释)

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

行内注释紧贴在被描述的代码的上一行,如果行内注释足够短,则可以处在同一行行尾(由一个空格隔开)。

所有行内注释都以 # 加一个空格开头。

应该限制行内注释的使用,因为它们的存在通常是一个代码异味的标志。

不要给显而易见的情况作行内注释:

  #
  x = x + 1 # x 自增

然而,行内注释在某些情况下是有用的:

  #
  x = x + 1 # 边界补足
## 命名规范(Naming Conventions)

使用 小驼峰命名法 (第一个词的首字母小写,后面每个词的首字母大写)来命名所有的变量、方法和对象属性。

使用 大驼峰命名法 (第一个词的首字母,以及后面每个词的首字母都大写)来命名所有的类 (在其他类似的命名法中,这种风格通常也被称为 帕斯卡命名法(PascalCase)大写驼峰命名法(CamelCaps)首字母大写命名法(CapWords)。)

(CoffeeScript 官方 约定是用驼峰命名法,因为这可以简化与 JavaScript 的相互转化,想了解更多,请看这里.)

对于常量,单词全部大写,用下划线隔开即可:

CONSTANT_LIKE_THIS

私有函数和私有变量都应该在前面加一个下划线:

_privateMethod: ->
## 函数(Functions)

(以下这些准则同样适用于类中的方法。)

当声明一个带参函数时,应在参数列表的右圆括号后空出一个空格:

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

无参函数不要用圆括号:

bar = -> #
bar = () -> #

当函数链式调用,却在一行放不下时,则把每个函数调用都另起一行,且都缩进一级(即在 . 前加两个空格)。

[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 # 大括号在适当的时候也可以去掉

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() # 单独初始化调用
(($ '#selektor').addClass 'klass').hide() # 全部调用

「函数体风格」并不得到推荐。但是, 当它适应一些特殊的项目需求时,还是得用它。

## 字符串(Strings)

用字符串插值代替字符串连接符:

'this is an #{adjective} string' #
'this is an ' + adjective + ' string' #

最好用单引号 ('') 而不是双引号 ("") 。除非是插入到另一段现有的字符串中(类似字符串插值)。

## 条件判断(Conditionals)

unless 来代替 if 的否定情况。

不要用 unless...else, 而用 if...else:

  #
  if true
    ...
  else
    ...

  #
  unless false
    ...
  else
    ...

多行的 if/else 语句应该缩进:

  #
  if true
    ...
  else
    ...

  #
  if true then ...
  else ...
## 循环和列表解析(Looping and 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
## 扩展本地对象(Extending Native Objects)

不要修改本地对象。

比如,不要给 Array.prototype 引入 Array#forEach

## 异常(Exceptions)

不要抑制异常抛出。

## 注解(Annotations)

必要的时候应该写注解,来指明接下来的代码块具体将干什么。

注解应紧贴在被描述代码的上一行。

注解关键字后面应该跟一个冒号加一个空格,加一个描述性的注释。

  # 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 里面。

## 其他(Miscellaneous)

and 更优于 &&.

or 更优于 ||.

is 更优于 ==.

not 更优于 !.

or= 应在可能的情况下使用:

temp or= {} #
temp = temp || {} #

最好用 (::) 访问对象的原型:

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...) -> #
You can’t perform that action at this time.