跳过导航

前言

有人说,编译器的规范叫做"语法规则"(grammar),这是程序员必须遵守的;而编译器忽略的部分,就叫"代码风格"(code style)。[1]

程序员固然可以自由选择代码风格,但是好的代码风格有助于写出质量更高、错误更少、更易于维护的程序。

"代码风格"的选择不应该基于个人爱好、熟悉程度、打字工作量等因素,而要考虑如何尽量使代码清晰易读、减少出错。你选择的,不是你喜欢的风格,而是一种能够清晰表达你的意图的风格。

如果你发现本规范中有任何不足,敬请指正或参与贡献。



“永远遵循同一套编码规范。”

  • 严格执行一致认可的风格 [2]
  • 如果有疑议,可以使用现有的、通用的模式
  • 别想着过早地优化代码。得先保证它们可读又可理解
  • 无论多少人参与及贡献,所有代码都应如同一个人编写的一样

“程序是给人读的,顺便让计算机执行。” —— Donald Knuth

“Programs are meant to be read by humans and only incidentally for computers to execute.” —— Donald Knuth


Global

使用2个空格缩进

在项目的所有代码中,应该只有一个风格。在缩进的使用上,必须始终保持一致。使用空格来提高可读性。

使用双引号

请不厌其烦地使用 双引号,虽然有时使用单引号更方便。


  <!-- HTML -->
  <h1 class="hello">Hello world!</h1>


  /* CSS */
  .selector[name="hello"]:before { /* 这里的引号不要省略 */
    content: "Hello World!";
    background: url(image.jpg); /* 这里的引号可以省略 */
    font-family: "Microsoft Yahei";
  }


  /* Javascript */
  var hello = "Hello World!";

      

适当添加空行

代码看起来应当像一系列可读的段落,而不是一大段揉在一起的连续文本。

具体情况需要自己把握,目的是为了更好地提升代码的可读性。


  /* Good */
  if (condition) {

    for (condition1; condition2; condition3) {
      var foo = 0;
      var bar = 1;

      // a comment
      if (condition) {
        alert(foo);
      } else {
        alert(bar);
      }
    }
  }

  /* Bad */
  if (condition) {
    for (condition1; condition2; condition3) {
      var foo = 0;
      var bar = 1;
      // a comment
      if (condition) {
        alert(foo);
      } else {
        alert(bar);
      }
    }
  }

      

文件头部的注释

良好的注释是非常重要的。请留出时间来描述组件(component)的工作方式、局限性和构建它们的方法。不要让你的团队其它成员来猜测一段不通用或不明显的代码的目的。


  <!-- (如果是 HTML 就加上这个)
  /**
   * ===========================================================
   *  文件标题
   * ===========================================================
   *
   *  从这里开始写这个文件的简要说明。
   *
   *  当需要进行更细致的解释说明、提供文档文本时,较长的说明文本就很
   *  有用了。这种长长的说明文本,可以包括示例 HTML、链接 等等其他你
   *  认为重要或者有用的东西。
   * 
   * ===========================================================
   *
   * @author (作者)
   * @create (创建时间)
   * 
   * @update (修改时间) (修改者)
   *   1. (修改内容)
   * @update (修改时间) (修改者)
   *   ...
   *
   * @todo: 这个“‘需做’陈述”描述了一个接下来要去做的小工作。这种文本,
   *       如果超长了的话,应该在80个半角字符(如英文)或40个全角字符
   *       (如中文)后便换行,并且保持对齐。
   */
  -->

      

编辑器配置

将你的编辑器按照下面的配置进行设置,以避免常见的代码不一致和差异:

参照文档并将这些配置信息添加到项目的.editorconfig文件中。例如 Bootstrap 中的实例 。更多信息请参考 EditorConfig


  # editorconfig.org

  root = true

  [*]
  charset = utf-8
  indent_size = 2
  end_of_line = lf
  indent_style = space
  insert_final_newline = true
  trim_trailing_whitespace = true

      

HTML

- 头部 -

文档类型

在HTML5出现之前,使用DOCTYPE不仅需要在多个版本之间作出选择,而且每一个版本的代码都很长,很难记忆。所以,请始终使用简洁的 HTML5 Doctype。


  <!DOCTYPE html>

      

语言属性

根据 HTML5 规范:

强烈建议为 html 根元素指定 lang 属性,从而为文档设置正确的语言。这将有助于语音合成工具确定其所应该采用的发音,有助于翻译工具确定其翻译时所应遵守的规则等等。

语言属性可以指明地区,其中与中文相关的有四个:[5]

如果需要指定使用繁体中文,应该使用 zh-Hant,毕竟使用繁体的不止一个地区,台湾和香港都使用繁体。要指定简体中文,则使用 zh-Hans

声明语言有利于搜索引擎优化。将页面语言声明为中文有利于网站在中文搜索引擎中的排名。百度是典型的中文搜索引擎,在其索引中,中文网站的权重远高于其他语言(包括英文)的网站。

更多关于 lang 属性的知识可以从 此规范 中了解,这里列出了 语言代码表


  <html lang="zh-CN">

      

字符编码

通过明确声明字符编码,能够确保浏览器快速并容易的判断页面内容的渲染方式。这样做的好处是,可以避免在 HTML 中使用字符实体标记(character entity),从而全部与文档编码一致(一般采用 UTF-8 编码)。


  <meta charset="UTF-8">

      

兼容模式

IE 支持通过特定的 <meta> 标签来确定绘制页面所采用的 IE 版本。除非有特殊需求,最好设置为 edge mode,从而通知 IE 采用其所支持的最新的模式。

阅读这篇 stack overflow 上的文章 可以获得更多有用的信息。


  <meta http-equiv="X-UA-Compatible" content="IE=Edge">

      

视觉区域

视觉区域(viewport)指的是浏览器(包括桌面浏览器和移动浏览器)显示页面的区域。它不包含浏览器地址栏、按钮这样的东西,只是浏览区域。为了让页面适应拥有不同大小视觉区域的浏览器,需要在 <head> 元素中添加视觉区域 <meta> 元素。[5]

其中,width=device-width 用以将视觉区域的宽度会被设成与设备宽度相同的值,从而保证媒体查询的 min-widthmax-width 特性拥有预期的效果。initial-scale=1.0 用以将页面的默认缩放级别设成100%。


  <meta name="viewport" content="width=device-width, initial-scale=1.0">

      

引入文件

根据 HTML5 规范(linkstylescript),在引入 CSS 和 JavaScript 时一般不需要指定 type 属性,因为 text/csstext/javascript 分别是它们的默认值。

另外,也不需要为 <script> 标签添加 lang 属性。


  <!-- Good -->
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>

  <!-- Bad -->
  <link type="text/css" rel="stylesheet" href="style.css">
  <script type="text/javascript" src="script.js"></script>

      

- 标签 -

标签规则

太多人觉得HTML太简单,但它恰恰又是前端开发中最基础最重要的部分。

尽量遵循 HTML 标准和语义,但是不要以牺牲实用性为代价。任何时候都要尽量使用最少的标签并保持最小的复杂度。


  <!-- Good -->
  <body>
    <ul>
      <li>
        <img src="images/image.jpg">
      </li>
      <li>
        "Not Character Entity"
      </li>
    </ul>
  </body>

  <!-- Bad -->
  <BODY>
    <UL>
      <li>
        <img src="images/image.jpg" />
      <li>
        &quot;Character Entity&quot;
    </UL>
  </BODY>

      

标签缩进

良好的缩进大大增强可读性。


  <!-- Good -->
  <!DOCTYPE html>
  <html lang="zh-cn">
  <head>
    <title>Page title</title>
  </head>
  <body>

    <h1 class="hello-world">Hello, world!</h1>
    <img src="images/image.jpg">

  </body>
  </html>

  <!-- Bad -->
  <!DOCTYPE html>
    <html lang="zh-cn">
      <head>
      <title>Page title</title>
    </head>
  <body>
    <h1 class="hello-world">Hello, world!</h1>
      <img src="images/image.jpg">
  </body>
  </html>

      

标签语义化

根据各个标签的用途而去使用它们。

使用标签要知道为什么去使用它们和是否正确。 例如,用 <h1> 标签构造标题, <p> 标签构造段落, <a> 标签构造锚点等。根据各个标签的用途而去使用是很重要的,它涉及到文档的可访问性、重用和代码效率等方面:

在前端web开发的上下文中,语义大多是与元素,属性,和属性值(包括像Microdata之类的扩展)的一致认同意义相关。这些认同意义通常在规范中被定义概念,它们可以帮助人类(程序员)或机器(搜索引擎)更好的理解网站中信息的不同方面。[4]


  <!-- Good -->
  <body>

    <header>
      <a href="recommendations">
        All recommendations
      </a>
    </header>

    <section>
      <article>...</article>
    </section>

    <footer></footer>

  <body>

  <!-- Bad -->
  <body>

    <div>
      <div onclick="goToRecommendations();">
        All recommendations
      </div>
    </div>

    <div>
      <div>...</div>
    </div>

    <div></div>

  <body>

      

- 属性 -

属性规则

使用标签时请尽量符合语义。


  <!-- Good -->
  <html lang="zh-cn">
  <body>
    <section data-role="dialog">
      <h1 class="hello-world">Hello, world!</h1>
      <img
        class="long-attr"
        data-role="example"
        src="images/image.jpg"
        width="100%">
    </section>
  </body>
  </html>

  <!-- Bad -->
  <html LANG="zh-cn">
  <body onload=init() >
    <section my-role=dialog>
      <h1 class="hello-world">Hello, world!</h1>
      <img class="long-attr" custom-role="example" src="images/image.jpg" style="width:100%">
    </section>
  </body>
  </html>

      

属性顺序

HTML 属性应当按照以下给出的顺序依次排列,确保代码的易读性。

class 用于标识高度可复用组件,因此应该排在首位。id 用于标识具体组件,应当谨慎使用(例如,页面内的书签),因此排在第二位。


  <a class="..." id="..." data-modal="toggle" href="#">...</a>

  <input class="form-control" type="text">

  <img src="..." alt="...">

      

布尔属性

布尔型属性可以在声明时不赋值。

XHTML 规范要求为其赋值,但是 HTML5 规范不需要(WhatWG section on boolean attributes):

元素的布尔型属性如果有值,就是 true,如果没有值,就是 false。

如果一定要为其赋值的话,请参考 WhatWG 规范:

如果属性存在,其值必须是 空字符串 或 区分大小写的规范名称,并且不要在收尾时添加空格。

简单来说,就是不用赋值。


  <!-- Good -->
  <input type="text" disabled>
  <input type="checkbox" value="1" checked>
  <option value="1" selected>1</option>

  <!-- Bad -->
  <input type="text" disabled="disabled">
  <input type="checkbox" value="1" checked="checked">
  <option value="1" selected="selected">1</option>

      

CSS

- 声明 -

声明规则


  /* Good */
  .selector,
  .selector-secondary,
  .selector[type="text"] {
    padding: 15px;
  }

  /* Bad */
  #selector, .selector-secondary, .selector[type=text] {
    padding:15px;
    margin:0px 0px 15px; }

      

单行声明

对于只包含一条声明的一组相关样式,为了易读性和便于快速编辑,建议将语句放在同一行。


  /* Good */
  .icon         { background-position: 0 0; }
  .icon-home    { background-position: 0 -20px; }
  .icon-account { background-position: 0 -40px; }

  /* Bad */
  .icon {
    background-position: 0 0;
  }
  .icon-home {
    background-position: 0 -20px;
  }
  .icon-account {
    background-position: 0 -40px;
  }

      

选择器

扩展阅读:


  /* Good */
  .avatar { ... }
  .tweet-header .username { ... }
  .tweet .avatar { ... }

  /* Bad */
  span { ... }
  .page-container #stream .stream-item .tweet .tweet-header .username { ... }
  .avatar { ... }

        

声明顺序

相关的属性声明应当归为一组,并按照下面的顺序排列:

  1. Positioning(定位)
  2. Box model(盒模型)
  3. Visual(外观)
  4. Typographic(文本)

由于定位可以从正常的文档流中移除元素,并且还能覆盖盒模型相关的样式,因此排在首位。盒模型排在第二位,因为它决定了组件的尺寸和位置。

其他属性只是影响组件的内部(inside)或者是不影响前两组属性,因此排在后面。

完整的属性列表及其排列顺序请参考 Recess


  .declaration-order {
    /* Positioning */
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 100;

    /* Box-model */
    display: block;
    float: right;
    width: 100px;
    height: 100px;

    /* Visual */
    background-color: #f5f5f5;
    border: 1px solid #e5e5e5;
    border-radius: 3px;

    /* Typography */
    font: normal 13px "Helvetica Neue", sans-serif;
    line-height: 1.5;
    color: #333;
    text-align: center;

    /* Misc */
    opacity: 1;
  }

      

- 属性 -

属性规则


  /* Good */
  .selector,
  .selector-secondary,
  .selector[type="text"] {
    padding: 15px;
    margin-bottom: 15px;
    background-color: rgba(0,0,0,.5);
    box-shadow:
      0 1px 2px #ccc,
      0 1px 0 #fff inset;
  }

  /* Bad */
  #selector, .selector-secondary, .selector[type=text] {
    padding:15px;
    margin:0px 0px 15px;
    background-color:rgba(0, 0, 0, 0.5);
    box-shadow:0px 1px 2px #CCC,inset 0 1px 0 #FFFFFF }

      

带前缀的属性

当使用特定厂商的带有前缀的属性时,通过缩进的方式,让每个属性的值在垂直方向对齐,这样便于多行编辑。


  /* Good */
  .selector {
    -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15);
            box-shadow: 0 1px 2px rgba(0,0,0,.15);
  }

  /* Bad */
  .selector {
    -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15);
    box-shadow: 0 1px 2px rgba(0,0,0,.15);
  }

      

多重属性值

对于以逗号分隔并且非常长的属性值 -- 例如一堆渐变或者阴影的声明 -- 可以放在多行中,这有助于提高可读性,并易于生成有效的区分。


  /* Good */
  .selector {
    box-shadow:
      1px 1px 1px #000,
      2px 2px 1px 1px #ccc inset;
    background-image:
      linear-gradient(#fff, #ccc),
      linear-gradient(#f3c, #4ec);
  }

  /* Bad */
  .selector {
    box-shadow: 1px 1px 1px #000, 2px 2px 1px 1px #ccc inset;
    background-image: linear-gradient(#fff, #ccc), linear-gradient(#f3c, #4ec);
  }

      

简写形式的属性

在需要显示地设置所有值的情况下,应当尽量限制使用简写形式的属性声明。常见的滥用简写属性声明的情况如下:

大部分情况下,我们不需要为简写形式的属性声明指定所有值。例如,HTML 的 heading 元素只需要设置上、下边距(margin)的值,因此,在必要的时候,只需覆盖这两个值就可以。过度使用简写形式的属性声明会导致代码混乱,并且会对属性值带来不必要的覆盖从而引起意外的副作用。

MDN(Mozilla Developer Network)上一篇非常好的关于 shorthand properties 的文章,对于不太熟悉简写属性声明及其行为的用户很有用。


  /* Good */
  .element {
    margin-bottom: 10px;
    background-color: red;
    background-image: url("image.jpg");
    border-top-left-radius: 3px;
    border-top-right-radius: 3px;
  }

  /* Bad */
  .element {
    margin: 0 0 10px;
    background: red;
    background: url("image.jpg");
    border-radius: 3px 3px 0 0;
  }

      

- 其他 -

class 命名

在为 Sass 和 Less 变量命名是也可以参考上面列出的各项规范。


  /* Good */
  .btn { ... }
  .btn-success { ... }
  .tweet { ... }
  .tweet-header { ... }

  /* Bad */
  .s { ... }
  .t_01 { ... }
  .red { ... }
  .header { ... }

      

不要使用 @import

<link>标签相比,@import指令要慢很多,不光增加了额外的请求次数,还会导致不可预料的问题。替代办法有以下几种:

请参考 Steve Souders 的文章 了解更多知识。


  <!-- Good -->
  <link rel="stylesheet" href="core.css">

  /* Bad */
  @import url("more.css");

      

媒体查询的位置


  .selector { ... }
  .selector-avatar { ... }

  @media (min-width: 480px) {
    .selector { ...}
    .selector-avatar { ... }
  }

      

Javascript [3]

- 基本 -

基本规则

根据上述风格来配置 .jshintrc 文件。


  /* Good */
  var foo = "Hello";
  var bar = "World";
  var obj = {
    foo: 0
  };

  function showMessage(a, b) {
    alert(a + " " + b);
  }

  obj.foo = 10;

  /* Bad */
  var foo = "Hello" // 没有分号
  var bar = "World" ; var obj = { // 两条语句在同一行
    foo:0
  }
  function showMessage(a,b) { // 逗号后面没有保留空格
    alert(a+" "+b) // 操作符左右没有保留空格
  }

      

其他建议

根据上述风格来配置 .jshintrc 文件。


  /* Good */
  var obj = { foo: 0 };

  obj.foo = 10;

  if (obj.foo === 10) {
    setTimeout(function () {
      alert("closure");
    }, 50);
  );

  /* Bad */
  var obj = new Object(); // 不要用构造函数创建

  with (obj) { // 不要使用 with
    foo = 10; // 这里创建了一个全局变量
  }

  if (obj.foo == 10) { // 尽量使用恒等式
    setTimeout("alert('eval')", 50); // 不要用字符串的方式执行语句
  }

      

声明

Javascript 中的声明可以在脚本的任何地方,但是它们实际上都会提前到包含这段逻辑的上下文的顶部执行。


  /* Good */
  var foo;
  var bar = true;

  if (condition) {
    var func = function () {
      ...
    };
  } else {
    var func = function () {
      ...
    };
  }

  /* Bad */
  var foo, bar = true,
    a = 1, b;

  if (condition) {
    function func () {
      ...
    };
  } else {
    function func () {
      ...
    };
  }

      

命名

Javascript 对大小写是敏感的。名字最好能与它的内容相对应,一般情况下要使用英文而不是拼音。


  /* Good */
  var myName = "Nicholas";
  var names = [0, 1, 2];
  var isSet = false;
  var MAX_COUNT = 10;

  function getName() {
    return myName;
  }

  function Person(name) {
    this.name = name;
  }
  Person.prototype.sayName = function () {
    alert(this.name);
  }

  /* Bad */
  var setName = "Nicholas";
  var name = [0, 1, 2];
  var set = false;
  var max_count = 10;

  function theName() {
    return setName;
  }

  function person(name) {
    this.name = name;
  }
  person.prototype.sayName = function () {
    alert(this.name);
  }

      

花括号

采用 Java 的风格。


  /* Good */
  if (conidion) {
    ...
  } else if {
    ...
  } else {
    ...
  }

  /* Bad */
  if (conidion) foo();

  if (conidion)
    foo();

  if (conidion) { foo(); }

  if (conidion)
  {
    foo();
  }

  if (conidion) {
    ...
  }
  else{
    ...
  }


      

圆括号


  /* Good */
  foo();
  bar(a, b);

  for (var i = 0; i < 9; i++) {
    ...
  }

  (function () {
    ...
  })();

  /* Bad */
  foo ();
  bar( a, b );

  for(var i = 0; i < 9; i++) {
    ...
  }

  function() {
    ...
  }();

      

- 直接量 -

字符串

与 Java、PHP 这些语言不同,Javascript 中的字符串使用双引号或单引号在功能上并无不同。但是我们需要关心的是,代码应当从头到尾只保持一种风格。


  /* Good */
  var foo = "Hello World!";
  var longString = "This is a long string " +
    "example.";

  /* Bad */
  var foo = 'Hello World!';
  var longString = "This is a long string \
    example.";

      

数字

在 Javascript 中,整数 和 浮点数 其实都存储为相同的数据类型。


  /* Good */
  var float = 10.00;
  var float = 0.01;
  var num = 8;

  /* Bad */
  var float = 10.;
  var float = .01;
  var num = 010; // 八进制写法

      

数组

使用数组直接量是定义数组最简洁的一种方式。


  /* Good */
  var values = [0, 1, 2];
  var longValues = [
    0,
    [0, 1, 2],
    {a: 1, b: 2}
  ];

  /* Bad */
  var values = [0,1,2];
  var values = [ 0, 1, 2 ];
  var longValues = [ 0, [0, 1, 2], {a: 1, b: 2} ];

      

对象

对象直接量可以高效地完成与非直接量写法相同的任务。


  /* Good */
  var obj = {
    foo: 0,
    bar: 1,
    obj: {a: 1, b: 2}
  }

  /* Bad */
  var obj = { foo: 0, bar: 0 }
  var obj = {
    "foo":0,
    bar :1
  }
  var obj = {
    foo: 0, bar: 0,
  }

      

null 和 undefined

null 常会与 undefined 混淆。其中一个让人颇感困惑之处在于 null == undefined 结果是 true。然而这两个值的用途却各不相同。

以下场景才应当使用 null

以下场景不应当使用 null

禁止使用特殊值 undefined


  /* Good */
  var person = null;

  if (person !== null) {
    doSomething();
  }

  function getPerson() {
    if (condition) {
      return new Person("Nicholas");
    } else {
      return null;
    }
  }

  console.log(person === null);
  console.log(typeof person === "undefined");

  /* Bad */
  var person;

  if (person !== null) { // person 未初始化
    doSomething();
  }

  function doSomething(arg1, arg2) {
    if (arg2 !== null) { // 不要用来检测参数
      doSomethingElse();
    }
  }

  console.log(person === undefined); // 不要直接使用 undefined

      

- 控制语句 -

switch 语句

尽管语法相似,Javascript 中任何表达式都可以合法地用于 case 从句,但在其他语言中则必须使用原始值和常量。


  /* Good */
  switch (conidion) {
    case 0:
      ...
      break;
    case 1:
      ...
      break;
    // no default
  }

  /* Bad */
  switch (conidion) {
  case 0 : ... break;
  case 1 :
  ...
  break;
  }

      

for 循环

传统的 for 循环,是从 C 和 Java 继承而来。


  /* Good */
  var i;

  for (i = 0; i < 9; i++ ) {
    if (i === 2) {
      process();
    }
  }

  /* Bad */
  for (var i = 0;i <9) {
    if (i !== 2) {
      continue;
    }
    process();
    i++;
  }

      

for-in 循环

for-in 循环不仅遍历对象的实例属性(instance property),同样还遍历从原型继承来的属性。


  /* Good */
  var prop;

  for (prop in object) {
    if (object.hasOwnProperty(prop)) {
      console(prop);
    }
  }

  /* Bad */
  var prop;
  var vals = [1, 2, 3];

  for (prop in object) {
    console(prop);
  }

  for (prop in vals) {
    console(prop);
  }

      

- 注释 -

注释规则

单行注释

单行注释以 // 开头。


  // a comment
  var foo = 0;

  var foo = 0;  // keep an indent

      

文档注释

遵循 JSDoc 规范。


  /**
   * A module representing a jacket.
   * 
   * @module jacket
   * @author devi
   */