Style Guide

代码风格是老生常谈,做代码评审、甚至普通的代码阅读,都会注意到其风格。最开心的莫过于看到代码 能够有章法,变量定义,函数声明,代码缩进,无不有其含义,无不助人理解其代码所作所为。然而它也 容易成为道德绑架的工具,两种不同的风格会掐架,最终导致无意义的争辩,本文无意染指这些,只是 说明一下我所采用的代码风格。

JavaScript

代码风格或者说规范,是件有点宗教意义的事情, 科班出身的程序员们,开始写代码没多久就会遇到这个问题,比如说我的第二门编程语言 C, 就有两个比较典型的缩进风格:

  • GNU Style
  • 1TBS(One True Brace Style)

前者看起来是这样的:

static char *
concat (char *s1, char *s2)
{
  while (x == y)
    {
      something ();
      somethingelse ();
    }
  finalthing ();
}

后者则是:

if (x < 0) {
    puts("Negative");
    negative(x);
} else {
    puts("Non-negative");
    nonnegative(x);
}

抛开缩进不谈,1TBS 最大的特点,是大括号中开括号的位置,是直接放在区块开端的行尾,而不是另起一行, 在阿里的前端世界里,它应该是最广为接受的,KISSY 用的即是这种。

花括号

我的选择是 1TBS,与 KISSY 保持一致,花括号的开括号放在行尾:

;(function() {
    // code
})()

分号

故事绝不会到此结束,鄙厂另外一个为人熟知的前端项目是 SeaJS,它的代码风格,是这样的:

function parseDependencies(code) {
  var ret = []

  code.replace(SLASH_RE, "")
      .replace(REQUIRE_RE, function(m, m1, m2) {
        if (m2) {
          ret.push(m2)
        }
      })

  return ret
}

和 1TBS 的明显区别是,两个空格缩进,其余基本相若。咦,有没有觉得代码干净那么一点点? 它的风格另一大特色,就是行尾的分号一律省略,只是在 [( 做行首时,加上分号,类似:

foo('bar')
;(function(d) {
    egg('ham')
})(document)

这里若不加分号,会被 JavaScript 引擎解析成 foo('bar')(function(d){ ... })(document)

  • 先调用 foo('bar')
  • 然后将它的返回值视作函数,把 function(d) {...} 作为参数,调用

如果它的返回值不是函数,当场就跪,那还算好;如果它的返回值偏偏是个函数,错在哪儿了都不一定知道。 也正是因为这个原因,Douglas Crockford 和 Google 推荐的 JS 代码风格里,表达式行尾一律加上分号, 尽管在绝大多数时候,上述情况并不易出现。

当然也有不吃 FUD 这一套的, 开始号召大家不要用了,有 写得很详细, 从 ECMAScript 规范解释省略分号是本意的,也有 行为艺术 的。

本文不讨论细节,只表述个人风格,我的选择是省略不必要的分号。

缩进

我用四个空格作为缩进。仍然用四个空格原因很简单,避免自己一不小心就写出回调金字塔, 平白多出许多无用的闭包,只能用 27” 的显示器写前端代码,等等。

                   //some codes
              })
         }
    })
}

正如 Linus Torvalds 曾经说的,如果你的代码得缩进好多层,从一个角度讲,也许意味着该好好重构了。

另外,也有好多人喜欢用制表符(tab)做缩进,并约定其宽度为四个或两个空格,jQuery 也是如此, 但我不采用这种。

变量声明

老道推荐一个函数一个 var,初衷是改变从C系程序员转变过来的前端工程师对 JavaScript 中区块的错误看法。JavaScript 里头,没有区块级别的变量声明,例如:

;(function() {
    var a = 1

    if (true) {
        var a = 2
    }

    console.log(a) // => 2
})()

限制只能使用单个 var,自然避免了这种困扰。但是这个用法带来的问题是,容易写出这种代码:

var a = this.foo(),
    b = this.bar(),
    c = this.egg(),
    d = this.ham()

这种写法,没法只在其中某一行下断点,只能在 var 起始的地方下,假如这个变量声明长得令人发指, 想要调试的话,是很恼人的。不可能很长?你看看 zepto 的风格就知道了, 天知道他们怎么调试的(Dijkstra 倒是说过,调试对解决臭虫的帮助并不大,它不过是慢动作演示一遍代码执行的过程而已,所以,或许大神们写代码, 从来都是不调试的吧)。

言归正传,我的风格是:

var a = this.foo()
var b = this.bar()

return Math.round((a + b) / 100)

这里还有个要注意的是,变量声明和表达式正文之间,空一行。

命名

至于变量名长度,全局或者范围比较广的变量,越能表达其作用越好,局部小函数体里的变量,不致歧义的情况下, 短名字也无不可。

类名请以大写字母开头,类似 SomeClassWithLongName

模块中的全局变量,或者模块名比较朴素,容易误覆盖的变量,可以用 _ 开头,例如:

var _path = require('path')
var _cache = {}

module.exports = function() {
    // code
}

普通变量名,使用 someVar 或者 some_var 均可,但在我的项目中,沿袭其核心数据格式的属性名规范, 使用下划线分隔字符,这是历史原因,不对其他项目做约束。

模块或者类暴露到外部的方法,统一使用驼峰,不可用 _,因为这些方法是不能被压缩工具优化掉的, cameCaselame_method 可以少一个字符,也更加符合 JavaScript 整体的风格一些。

留白

if (typeof a === 'undefined') {
    // code
}
while (i++ !== 8848) {
    climb()
}
for (var i = 0; i < 365; i++) {
    eat()
    sleep()
}

这些流程控制语句,ifwhilefor 与后边的开括号要有个空格,闭括号与花括号的开括号也是, 还有其他一些地方,也都要留白。总结一下规律,除了函数调用和匿名函数声明,括号前都要留白。 不用留白的例子:

var foo = function(a, b) {}
foo()

匿名函数声明时,大括号的开括号前仍然要留的。

错误示例:

// 错误:if、=== 及相关分隔符号之间要有留白
if(typeof a==='undefined'){
    //code
    // 错误:注释符号后边也得留个空白哦
}
// 同上
while(i++!==8848){
    climb()
}
// 同上
for(var i=0;i<365;i++){
    eat();sleep()
}

另外,请不要在行尾有多余的空格。

单行区块

前面的例子里,除了 for 循环,ifwhile 还可以写成:

if (typeof a === 'undefined') findA()

while (i++ !== 8848) climb()

请不要写成:

// 如果区块代码另起一行,请别忘了大括号
if (typeof a === 'undefined')
    findA()

// 如果只在一行内,大括号是不必要的,别画蛇添足啦
while (i++ !== 8848) { clibm() }

else

if (a > 100) {
    // 爆表啦!
}
else if (a < 0) {
    // 负分!滚粗!
}
else {
    // 同学你最乖了
}

else 另起一行,这样条件的各个分支区块会清楚许多。

对象字面量

module.exports = {
    propFoo: 'aha',
    propBar: true,
    propArray: ['a', 'b', 'c'],
    propAnotherArray: 'what a wonderful world'.split(' ')
}

冒号后头一个空格即可。

Node 的逗号前置风格,太另类了,还是不采用吧。

CSS 与 SCSS

CSS 的缩进风格与 JavaScript 相似,不同的是这里我们采用两个空格宽度:

.content {
  padding: 5px 10px;

  .title {
    font-weight: bold;
  }
}

需要有浏览器厂商前缀(vender prefix)的声明,按长短循序排列:

@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
     -moz-border-radius: $radius;
          border-radius: $radius;
}

HTML

请使用 HTML5,两个空格缩进:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>HTML Skeleton</title>
</head>
<body>
  <!-- your content goes here -->
  <script>
  // JavaScript 代码的缩进,仍然是四个空格
  ;(function() {
      console.log('foobar')
  })()
  </script>
</body>
</html>

以上,都是个人的风格。代码风格的讨论,是很容易引起口水的。最妥帖的原则应该是在已有项目中统一使用已有的规范, 在新项目中则尽早确立。维持项目风格的一致性,才是最重要的,其余只是口水。