# CoffeeScript 编码风格指南（译）

2014-06-21 GeekPlux

这份指南阐述了一些 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?）
  - 最大行宽（Maximum Line Length）
  - 空行（Blank Lines）
  - 结尾空白（Trailing Whitespace）
  - 可选的逗号（Optional Commas）
  - 编码（Encoding）
- 模块导入（Module Imports）
- 表达式和语句中的空白（Whitespace in Expressions and Statements）
- 注释（Comments）
  - 块注释（Block Comments）
  - 行内注释（Inline Comments）
- 命名规范（Naming Conventions）
- 函数（Functions）
- 字符串（Strings）
- 条件判断（Conditionals）
- 循环和列表解析（Looping and Comprehensions）
- 扩展本地对象（Extending Native Objects）
- 异常（Exceptions）
- 注解（Annotations）
- 其他（Miscellaneous）

## 代码布局（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...) -> # 好

（译文完）

- 原文地址
- 中文版地址
- 原 Github 地址
- 中文版 Github 地址

感谢阅读，如果觉得有启发，请通过我的 Donate 页面，赞助本文。

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。

Category: Web CoffeeScript
Tags: #Web#CoffeeScript#译文#Translation
上一篇

CoffeeScript 笔记

下一篇

双拼学习记

Please leave comments (点击写下你的评论)