跳过导航

前言 [1]

规范代码风格的目的,是使代码在多人协作的场景下具有一致性。关于如何解决一般性的问题,是不包括在风格规范中的,这是编程实践中的内容。

编程实践是另外一类规范。代码风格只关心代码的呈现,而编程实践关心代码的结果。你可以将编程实践看作是“秘方” —— 它们指引开发者以某种方式编写代码,以达到更好的结果。

如果你使用过一些设计模式,比如 MVC 中的观察者模式,那么你已经对编程实践很熟悉了。设计模式是编程实践的组成部分,专门解决软件组织方面的问题。



“构建软件设计的方法有两种:

  • 一种是把软件做得很简单以至于明显找不到缺陷;
  • 另一种是把它做得很复杂以至于找不到明显的缺陷。”

                  —— C.A.R. Hoare

Global

- 解耦 -

保持松耦合!

如果两个组件耦合太紧,则说明一个组件和另一个组件直接相关,这样的话,修改一个组件的逻辑,另外一个组件也需要修改。想象一下,如果一个系统包含上百个组件,那这简直就是一场噩梦。

在 Web 开发中,用户界面是由三个彼此隔离又相互作用的层定义的:

当能够做到修改一个组件而不需要更改其他的组件时,就做到了松耦合。这对于代码的可维护性来说至关重要。

CSS Javascript
HTML

HTML 解耦


  <!-- Good -->
  <head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
  </head>
  <body>
    <a href="javascript:;">...</a>
  </body>

  <!-- Bad -->
  <head>
    <style>
      body {...}
    </style>
    <script>
      function init () {...}
    </script>
  </head>
  <body style="background:#f7f7f7;" onload="init();">
    <a href="javascript:reload();">...</a>
  </body>

      

CSS 解耦


  /* Good */
  .box-block {
    width: 100%;
  }

  /* Bad */
  #box-block {
    width: expression(this.parentNode.clientWidth);
  }

      

Javascript 解耦


  <!-- HTML -->
  <div id="box"></div>
  <script id="tmp" type="text/html">
    <h1>A template content.</h1>
  </script>


  /* Good */
  box.inerHTML = document.getElemnetById("tmp").innerHTML;
  box.className = "box-block";

  /* Bad */
  box.inerHTML = "<h1>A template content.</h1>";
  box.style.cssText = "width:100%";

      

- 兼容 -

优雅降级 & 渐进增强

目前有两种浏览器兼容策略:

“渐进增强”观点认为应关注于内容本身。[3]

内容是我们建立网站的诱因。有的网站展示它,有的则收集它,有的寻求,有的操作,还有的网站甚至会包含以上的种种,但相同点是它们全都涉及到内容。

这使得“渐进增强”成为一种更为合理的设计范例。这也是它立即被 Yahoo! 所采纳并用以构建其 “分级式浏览器支持 (Graded Browser Support)” 策略的原因所在。

虽然如此,策略的使用在具体情况中应该视情况而定:

使旧版本 IE 支持 HTML5

IE浏览器是前端开发者永远的痛,IE6/7/8 对于 HTML5 没有任何的支持。好消息是你可以引用一个很小的库,使得 IE6/7/8 支持 HTML5。


  <!--[if lt IE 9]>
    <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->

      

使用 Normalize.css

Normalize.css 是为抹平不同浏览器之间差异的基准CSS。是由 Nicolas GallagherJonathan Neal 维护的项目,该项目已被 Twitter BootstrapHTML5 Boilerplate 等多个顶尖的前端框架所用。[8]

避免使用 CSS Hack

请先尝试使用其他的解决方法,比如分离一个针对 IE 的 CSS 文件。

虽然 Hack 很有诱惑力,可以当作浏览器检测或特殊的 CSS 过滤器,但它的行为太过于频繁,会长期伤害项目的效率和代码管理,所以应尽量避免。


  /* Good */
  .selector {
    max-width: 100px;
  }

  .ie-6 .selector {
    width: 100px;
  }

  /* Bad */
  .selector {
    max-width: 100px;
    _width: 100px;
  }

      

避免特性/浏览器推断

“特性推断”根据一个特性的存在推断另一个特性是否存在。问题是,推断是假设并非事实,而且可能会导致维护性的问题。

你不能从一个特性的存在推断出另一个特性是否存在。最好的情况下两者有薄弱的联系,最坏的情况下两者根本没有直接关系。

除了不要进行推断,在实际开发中也应尽量使用特性检测来取代浏览器检测。


  /* Good */
  if (getElementById in document) {
    element = document.getElementById(id);
  }

  isIE = navigator.userAgent.indexOf("MSIE") > -1;

  /* Bad */
  if (document.getElementsByTagName) { // 特性推断
    element = document.getElementById(id);
  }

  isIE = !!document.all; // 浏览器推断

      

Shim/Shiv 和 Polyfills

Polyfill 就是一种用在浏览器 API 上的 Shim:

在实际中为了方便做对比,会特指 Shim 的 API 不是遵循标准的,而是自己设计的。


  // 一段实现 querySelectorAll 的 Polyfill
  if (!document.querySelectorAll) {
    Element.prototype.querySelectorAll = function (q) {
      return $(this).find(q).get();
    };
    document.querySelectorAll = Element.prototype.querySelectorAll;
  }

      

使用 Modernizr

作为一个开源的 JavaScript 库,Modernizr 检测浏览器对 CSS3 或 HTML5 功能支持情况。 Modernizr 并非试图添加老版本浏览器不支持的功能,而是令你通过创建可选风格配置修改页面设计。 它也可以通过加载定制的脚本来模拟老版本浏览器不支持的功能。[6]

只需要到 这个页面 选中你打算使用的 HTML5 元素和 CSS3 属性,然后下载定制后的 Javascript 文件。最后在你的页面中引入这个 Javascript 文件,剩下的就交给 Modernizr 去做吧。

Modernizr 会根据浏览器的功能情况在页面的 <html> 标签上添加一组类;它也可轻松实现 JavaScript 解决方案,即人们熟知的 Polyfills。

Modernizr 简单易用,但不是万能的。 成功使用 Modernizr 很大程度上取决于你的 CSS 和 JavaScript 技能。


  <!-- HTML -->
  <!-- 根据浏览器支持的特性自动生成类名 -->
  <html class="js no-touch cssgradients">
  <head>
    <script src="path/to/modernizr.js">
  </head>
  <body>
    <!-- 已包含 HTML5 shiv,可以实现 IE 浏览器对 HTML5 标签的支持 -->
    <header>
      ...
    </header>
  </body>
  </html>


  /* CSS */
  .no-js .glossy,
  .no-cssgradients .glossy {
      background: url(images/glossybutton.png);
  }
  .cssgradients .glossy {
      background-image: linear-gradient(top, #555, #333);
  }


  /* Javascript */
  Modernizr.load({
    test: Modernizr.geolocation,
    yep : "geo.js",
    nope: "geo-polyfill.js"
  });

      

HTML

- 架构 -

结构设计

HTML 不像 Javascript 和 CSS 那么精彩,其作用最被忽视,但它又是最基础的载体。HTML的难点在结构的设计上,需要稳定、灵活、友好、易理解。其实 HTML/CSS 完全受意识流控制。如何架构好,源于经验而非知识。[7]

HTML 的结构从某个方面来说依赖于类名的定义:

太多人觉得HTML太简单,但它恰恰又是前端开发中最基础最重要的部分。HTML结构设计的合不合理,直接影响到代码易不易维护,灵不灵活,同时事关网页性能,协作效率。[8]


  <!-- Good -->
  <div class="module">
    <h2 class="module-head">News</h2>
    <ol class="module-list">
      <li>First news title.</li>
      <li>Second news title.</li>
      ...
    </ol>
    <p>Lorem ipsum dolor sit amet...</p> <!-- 在 CSS 中设置 text-indent -->
    <p>Lorem ipsum dolor sit amet...</p>
    ...
  </div>

  <!-- Bad -->
  <div class="news"> <!-- 类名不应命名成内容相关的(结构-内容) -->
    <h2 class="red">News</h2> <!-- 类名不应命名成样式相关的(结构-样式) -->
    <div class="list"> <!-- 类名缺乏关联性 -->
      <p>1. First news title.</p> <!-- 不应手写编号(结构-内容) -->
      <p>2. Second news title.</p>
      ...
    </div>
    <p>&nbsp;&nbsp;Lorem ipsum dolor sit amet...</p> <!-- 不应使用空格缩进(内容-样式) -->
    <p>&nbsp;&nbsp;Lorem ipsum dolor sit amet...</p>
    ...
  </div>


      

减少标签的使用

标签使用的越少越好,事实也证实了确实如此。如果想创建具有良好结构的文档,那就需要考虑尽量少的使用标签。

当然我们所说的是必要与不必要,假如必须需要某种容器来布局,那是不可以省略的。


  <!-- Good -->
  <img class="avatar" src="...">

  <!-- Bad -->
  <span class="avatar">
    <img src="...">
  </span>

      

可重用及可组合的组件

一个灵活且可重用的组件:

这部分可以参考 Bootstrap


  <!-- 一个面包屑组件(Bootstrap) -->
  <ol class="breadcrumb">
    <li><a href="#">Home</a></li>
    <li><a href="#">Library</a></li>
    <li class="active">Data</li>
  </ol>


      

组件修饰器

从基础组件出发,通过修饰器,会产生有些许外观差异的变体组件。比如,不同颜色的背景或边框。有两种主要的模式被用来创建这些不同的组件:[9]

同 Bootstrap 一样,建议使用 “多类”模式


  <!-- 单类模式 -->
  <button class="btn">Default</button>
  <button class="btn-primary">Login</button>

  .btn, .btn-primary { /* base styles */ }
  .btn-primary { /* specific styles */ }

  <!-- 多类模式 -->
  <button class="btn">Default</button>
  <button class="btn btn-primary">Login</button>

  .btn { /* base styles */ }
  .btn-primary { /* specific styles */ }

      

结构化类名

在创建组件及其变体时,有些类被用作组件边界(btn、btn-group),有些被用作修饰器(btn-primary),有些被用作子对象(btn-group-item)。很难弄清楚它们之间的关系,因为这些命名并没有清楚的揭示这些类的目的,没有统一的模式。

在这方面不得不提的是 BEM(Block, Element, Modifier)。BEM 是一种方法论(不是规范!),是由 Yandex 团队提出的一种类命名方法。能让团队成员通过统一的命名模式:块(blocks)、元素(elements)、修饰符(modifiers)来进行合作和思想沟通。

先看看 @kejun评论

“我非常认同这种设计思想。但我还是不会照搬它的命名规则。太TM囧了!”

所以在这里提出只是抛砖引玉的作用,具体应用有待商讨。


  <!-- BEM -->
  <ul class="block-name">
    <li class="block-name__element-name--modifier-name">...</li>
    <li class="block-name__element-name">...</li>
  </ul>

  <ul class="block-name--modifier-name">
    <li class="block-name--modifier-name__element-name--modifier-name">...</li>
    <li class="block-name--modifier-name__element-name">...</li>
  </ul>

      

- 可访问性 -

跳过导航

如果你的导航部分包含很多链接,并且在DOM结构上也是排列在主内容之前,那么建议在紧跟 <body> 标签之后添加一个 跳过导航 的链接。(这里解释了这样做的原因[10]


关于可访问性可以在这里了解更多:


  <!-- Good -->
  <body>
    <a href="#content" class="sr-only">Skip to main content</a>

    <nav>
      Multi navigation.
    </nav>

    <div class="container" id="content">
      The main page content.
    </div>
  </body>

      

标题嵌套

当标题嵌套时(<h1> - <h6>),你的文档的主标题应该是<h1>。随后的标题逻辑上就应该使用<h2> - <h6>,这样,屏幕阅读器就可以构造出页面的内容列表。[10]

从这里了解更多:HTML CodeSnifferPenn State's AccessAbility

  
    <!-- Good -->
    <h1>Main title</h1>

    <h2>First level</h2>
    <h3>Second level</h3>
    ...
    <h2>First level</h2>
    <h3>Second level</h3>
    ...
  
          

避免添加 tabindex

tabindex 的目的是解决屏幕阅读器读取内容的顺序问题。

例如有人把“忘记密码”的链接放在密码框后面,导致密码输入完毕后,按 tab 键的时候,不是在提交按钮上,而是“忘记密码”链接上,此时,就需要借助 tabindex 确定更好的阅读顺序(在WCAG 2.0中这实际上被称为“焦点顺序”)。

然而,在大部分情况下,tabindex 只会是事情变得更加混乱,会让用户走非正常逻辑。[11]


  <!-- Bad -->
  <form>
    <input name="password" type="password" tabindex="1">
    <input name="password" type="password" tabindex="2">
    <a href="...">Forget password?</a>
    <button type="sublmit" tabindex="3"></button>
  </form>

      

不要写 onfocus="this.blur()"

我们常用这样的代码来去除链接取得焦点时外围出现的虚线框,Google 一下,前面几十页谈的都是这个去除虚线框的技巧。[12]

但我们也许以前从未想过:我们的这行代码给盲人用户们带来了巨大的困扰:这中断了盲人用户的Tab键路径,导致Tab光标无法聚焦页面的下一个控制器(链接、表单域、object、image map等)。


  /* Good */
  a:focus {
    outline: none;
  }


  <!-- Good -->
  <a href="..." hidefocus>...</a>

  <!-- Bad -->
  <a href="..." onfocus="this.blur()">...</a>


  /* Bad */
  $("a").on("focus", function () {
    this.blur();
  });

      

使用 ARIA

WAI-ARIA(Web Accessibility Initiative’s Accessible Rich Internet Applications),全称“Web可访问性倡议之可访问的富互联网应用”,是一种技术规范,自称为“有桥梁作用的技术”。之所以这样说,是因为在HTML提供相应的语义功能之前,它可以用来填补一些语义上的空白。[13]

使用 ARIA 地标角色可以将页面的主要区域清晰地划分出来,因为它的重要作用就是提高页面的可访问性,尤其是对屏幕阅读器的可访问性。此外,由于在 CSS 选择器中很容易使用 ARIA 的 role 属性,因此,它可以有效地避免像 id="content"id="sidebar" 这样的属性。

地标角色只是 ARIA规范 众多特性的一种。不同屏幕阅读器对地标角色的支持情况见 html5accessibility.com 网站上的 测试结果


  <!-- Good -->
  <header role="banner">
    <!-- Logo, Nav... -->
  </header>

      

CSS

- 效率 -

使用继承 [14]

使用继承可以避免重复的声明,常用于文字样式。


  /* Good */
  body { font-family: arial, helvetica, sans-serif; }

  /* Bad */
  p { font-family: arial, helvetica, sans-serif; }
  div { font-family: arial, helvetica, sans-serif; }
  .selector { font-family: arial, helvetica, sans-serif; }

      

使用多重选择器

同样地,用多重选择器来避免重复代码。


  /* Good */
  h1, h2, h3 { color: #333; }

  /* Bad */
  h1 { color: #333; }
  h2 { color: #333; }
  h3 { color: #333; }

      

使用多重声明

为了更好的组织代码,不要将同一个选择器的样式声明散落在不同的地方。


  /* Good */
  p {
    margin: 0;
    background: #ddd;
    color: #666;
  }

  /* Bad */
  p { margin: 0; }
  p { background: #ddd; }
  p { color: #666; }

      

使用简记属性

若要一条条地显式地指明所有属性,代码就会变得很冗长。使用简记规则的意义正在于此:彻底地减少这样负担。


  /* Good */
  .selector {
    margin: 1rem;
    font: 1rem/1.5 arial;
  }

  /* Bad */
  .selector {
    margin-top: 1rem;
    margin-right: 1rem;
    margin-bottom: 1rem;
    margin-left: 1rem;
    font-size: 1rem;
    font-family: arial;
    line-height: 1.5;
  }

      

避免 !important

!important 可以帮助我们轻松地覆盖样式,但是非到万不得已不要用 !important[15]

如果你是出于懒惰使用 !important,为了避免例行的调试而滥用它,那么你(或者是那些后继开发你项目的人)将会深受其害。

如果你并非滥用只是有偶尔用一下!important,同样,你很快就会发现你的样式会难以维护。


  /* Good */
  .selector.special {
    background-color: #ddd;
  }

  /* Bad */
  .selector {
    background-color: #ddd !important;
  }

      

- 架构 -

良好的 CSS 架构 [16]


“Code for system, Not for pages.” —— Liang Bin Hsueh [17]

参考 OOCSS

OOCSS(Object Oriented CSS)的概念最早是由 Nicole Sullivan 提出。

它有两个主要的原则:

Github 地址 在这里


  /* Good */
  .box {
    overflow: hidden;
    width: 400px;
  }

  .widget {
    overflow: auto;
    width: 500px;
    min-height: 200px;
  }

  .skin {
    border: solid 1px #ccc;
    background: linear-gradient(#ccc, #222);
    box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
  }

  /* Bad */
  #box {
    overflow: hidden;
    width: 400px;
    border: solid 1px #ccc;
    background: linear-gradient(#ccc, #222);
    box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
  }

  #widget {
    overflow: auto;
    width: 500px;
    min-height: 200px;
    border: solid 1px #ccc;
    background: linear-gradient(#ccc, #222);
    box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
  }

      

参考 SMACSS

SMACSS(Scalable and Modular Architecture for CSS)是 Jonathan Snook 身经百战的经验总结。它没有库,而是 一本书,一套经验与思想。

它定义了五种样式类型:

SMACSS 有两个核心目标:


  /* Base */
  html, body { ... }
  input[type="text"] { ... }
  a:hover { ... }

  /* Layout */
  .col-md-6 { ... }
  .col-sm-4 { ... }


  /* Module */
  .site-head { ... }
  .site-foot { ... }

  /* State */
  .active { ... }
  .hide { ... }

  /* Theme */
  /* in module.css */
  .mod { border: 1px solid; }
  /* in theme.css */
  .mod { border-color: blue; }

      

- 维护 -

样式表索引

样式表头部索引定义可以帮助你和其它人弄清楚该样式表文件的相关信息,它一般是一段格式化的CSS注释文本。[19]


  /*
   * STRUCTRUE
   * -------------------------
   * Page width: 980px
   * ...
   * 
   * CORLORS
   * -------------------------
   * Body background: #def455
   * Main text:       #333
   * Links:           #00600f
   * Visited links:   #098761
   * Hover links:     #aaf433
   * ...
   */

      

注释锚点

在文档注释中命名锚点是用来规划整个CSS文件结构的(就好像书签一样),从而使整个CSS文件获得良好的组织。[19]


  /*
   * STRUCTRUE ANCHOR
   * -------------------------
   * header    Header Definitions
   *   menu      Global Site Navigation
   * content   Main content
   * footer    Footer Definitions
   */

  /* header */
  ...
  /* content */
  ...
  /* footer */
  ...

      

代码组织


  /*
   * Component section heading
   */
  .element { ... }

  /*
   * Component section heading
   *
   * Sometimes you need to include optional context for the entire component. Do that up here if it's important enough.
   */
  .element { ... }

  /* Contextual sub-component or modifer */
  .element-heading { ... }

      

预处理

不同的CSS预处理有着不同的特性、功能以及语法。编码习惯应当根据使用的预处理程序进行扩展,以适应其特有的功能。推荐在使用SCSS时遵守以下指导。


  .selector-1 {
    @extend .other-rule;
    @include clearfix();
    @include box-sizing(border-box);
    width: x-grid-unit(1);
    // other declarations
  }

  // Without nesting
  .table > thead > tr > th { ... }
  .table > thead > tr > td { ... }

  // With nesting
  .table > thead > tr {
    > th { ... }
    > td { ... }
  }

      

Javascript [1]

- 全局变量 -

避免使用全局变量

随着代码的增长,全局变量毫无疑问的会导致一些非常重要的可维护性问题:


  /* Bad */
  function sayColor() {
    alert(color); // color 从哪里来?
  }

      

意外创建的全局变量

Javascript 中有很多陷阱,会使我们不小心就创建了全局变量。因为意外创建全局变量并不会引起报错,有时很难察觉到。

这时,我们应该使用 JSLintJSHint(推荐) 来检查代码。


  /* Bad */
  function doSomething() {
    var count = 10;
        name = "Nicholas"; // 意外创建的全局变量
    ...
  }

      

使用单全局变量

依赖尽可能少的全局变量是一个好方法。单全局变量模式已经在各种流行的库中广泛使用了:

“单全局变量”的意思是,这个全局对象名是唯一的(不会导致冲突),并将所有功能代码都挂载到这个全局对象上。因此每个可能的全局变量,都变成这个唯一全局对象的属性,从而不会创建多个全局变量。


  /* Good */
  // 单全局变量
  var global = {
    doSomething: function() {...},
    doSomethingElse: function() {...}
  };

  /* Bad */
  // 暴露在全局环境下
  var doSomething = function() {...}
  var doSomethingElse = function() {...}

      

使用命名空间

即使代码只有一个全局对象,也存在着全局污染的可能性。大多数使用单全局变量模式的项目同样包含“命名空间”的概念。

“命名空间”是简单的通过全局对象的单一属性表示的功能性分组。将功能按照命名空间进行分组,可以让你的单全局对象变得井然有序。


  /* Good */
  var global = {
    // 命名空间
    myNamespace: function(ns) {
      doSomething: function() {...}
    },
    yourNamespace: function(ns) {
      doSomething: function() {...} // 不会冲突
    }
  };

  /* Bad */
  var global = {
    doSomething: function() {...},
    ...
    // 很长的代码
    ...
    doSomething: function() {...} // 别人不小心造成的命名冲突
  };

      

零全局变量

通过闭包我们可以实现零全局变量。这种方法应用场景不多,最常见的情形就是一段不会被其他代码访问的完全独立的代码。之所以存在这种情形,是因为所有所需的代码都会合并到一个文件。

这种称之为“函数包装器(function wrapper)”的代码片段,可用于任何不需要创建全局对象的场景:

以下场景不适合使用零全局变量的方式:


  /* Good */
  (function (win) {

    "use strict";

    var doc = win.document;

    ...

  })(window);

      

使用模块化(推荐)

现在已经出现了许多前端模块化的规范:[20]

推荐使用 ES6Module 语法importexport


  /* Good */
  // 模块化(sea.js)
  define(function(require, exports, module) {
    ...
    module.exports = {
      ...
    };
  });

      

- 事件 -

事件处理

大多数前端工程师需要花费很多时间来编写和修改事件处理程序。遗憾的是,在 Javascript 诞生之初,这部分内容并未受太多重视。甚至当开发者们热衷于将传统的软件架构概念融入到 Javascript 里时,事件绑定仍然没有受到多大重视。

大多数与事件处理相关的代码和环境紧紧耦合在一起,导致可维护性很糟糕。为此我们应当采用适当的方法来避免。


  /* Bad */
  $(element).on("click", function (event) {
    element.style.left = event.clientX + "px";
    element.style.top = event.clientY + "px";
  });

  /* Not so bad */
  var handleClick = function (event) {
    element.style.left = event.clientX + "px";
    element.style.top = event.clientY + "px";
  });

  $(element).on("click", handleClick});

      

隔离应用逻辑

应用逻辑(application logic)是和应用相关的功能性代码,而不是和用户行为相关的。将应用逻辑从事件程序中抽离出来是一种最佳实践,因为它也许会在其他场景下运行:


  /* Not so good */
  var myApp = {
    // 事件处理
    handleClick = function (event) {
      this.moveElement(event);
    },
    // 应用逻辑
    moveElement = function (event) {
      element.style.left = event.clientX + "px";
      element.style.top = event.clientY + "px";
    },
  };

  // 事件绑定
  $(element).on("click", myApp.hanldeClick);

      

不要分发事件对象

应用逻辑不应当依赖于 event 对象来完成正确功能,原因如下:

最佳的办法是让事件处理程序使用 event 对象来处理事件,然后把其中需要的数据传给应用逻辑。


  /* Good */
  var myApp = {
    // 事件处理
    handleClick = function (event) {
      event.stopPropagation();
      this.moveElement(event.clientX, event.clientY);
    },
    // 应用逻辑
    moveElement = function (x, y) { // 传入明确的参数
      element.style.left = x + "px";
      element.style.top = y + "px";
    },
  };

  // 事件绑定
  $(element).on("click", myApp.hanldeClick);

      

- 其他 -

将配置数据分离出来

配置数据是应用中写死(hardcoded)的值,而数据是不应当影响指令的正常运行的。比如:

配置数据最好放在单独的文件中,以便清晰地分隔数据和应用逻辑。推荐使用 JSON 格式。


  /* Bad */
  function validat(value) {
    if (!value) {
      alert("Invalid value");
      location.href = "/errors/invalid.php";
    }
  }

  /* Good */
  // 抽离数据
  var config = {
    MSG_INVALID: "Invalid value",
    URL_INVALID: "/errors/invalid.php"
  };

  function validat(value) {
    if (!value) {
      alert(MSG_INVALID);
      location.href = URL_INVALID;
    }
  }

  // 放入文件中
  {
    "MSG_INVALID": "Invalid value",
    "URL_INVALID": "/errors/invalid.php"
  }

      

抛出自定义错误

编程语言有“创建”错误的能力确实令人迷惑。然而,Nicholas 大牛告诉我们,在积累了大量的经验之后,他已然很热衷于抛出自己的错误。

关于何时抛出错误,有一些经验可以借鉴:

ECMA-262 规范指出了7种错误类型。当不同错误条件发生时,可以帮助更好地处理错误。


  /* Good */
  try {
    // Some code tigger Error
  } catch (ex) {
    if (ex instanceof TypeError) {
      // Handle TypeError
    } else if (ex instanceof ReferenceError) {
      // Handle ReferenceError
    } else  {
      // Handle other Error
    }
  }

      

不是你的对象不要动

当你的代码创建了这些对象时,你拥有这些对象。创建对象的代码也许没必要一定由你来写,但只要维护代码是你的责任,那么就是你拥有这些对象。比如,YUI 团队拥有该 YUI 对象,Dojo 团队拥有该 dojo 对象,即使原作者离开了,各自对应的团队仍然是这些对象的拥有者。

请牢记,如果你的代码没有创建这些对象,不要修改它们,包括:

对它们要做到:


  /* Bad */
  // 在DOM 对象上增加了方法
  document.sayImAwesome = function () {
    slert("You're awesome.");
  }

  // 在原生对象上增加了方法
  Array.prototype.reverseSort = function () {
    return this.sort().reverse();
  }

  // 在库对象上增加了方法
  YUI.doSomething = function () {
    ...
  }