最佳实践
可维护性
什么是可维护的代码
通常,说代码“可维护”就意味着它具备如下特点。
容易理解:无须求助原始开发者,任何人一看代码就知道它是干什么的,以及它是怎么实现的。
符合常识:代码中的一切都显得顺理成章,无论操作有多么复杂。
容易适配:即使数据发生变化也不用完全重写。
容易扩展:代码架构经过认真设计,支持未来扩展核心功能。
容易调试:出问题时,代码可以给出明确的信息,通过它能直接定位问题。
编码规范
可读性
要想让代码容易维护,首先必须使其可读。可读性必须考虑代码是一种文本文件。为此,代码缩进 是保证可读性的重要基础。如果所有人都使用相同的缩进,整个项目的代码就会更容易让人看懂。缩进 通常要使用空格数而不是 Tab(制表符)来定义,因为后者在不同文本编辑器中的显示不同。一般来说, 缩进是 4 个空格,当然具体多少个可以自己定。 可读性的另一方面是代码注释。在大多数编程语言中,广泛接受的做法是为每个方法都编写注释。 因为 JavaScript 可以在代码中的任何地方创建函数,所以这一点经常被忽视。正因为如此,可能给 JavaScript 中的每个函数都写注释才更重要。一般来说,以下这些地方应该写注释。
函数和方法。每个函数和方法都应该有注释来描述其用途,以及完成任务所用的算法。同时, 也写清使用这个函数或方法的前提(假设)、每个参数的含义,以及函数是否返回值(因为通过 函数定义看不出来)。
大型代码块。多行代码但用于完成单一任务的,应该在前面给出注释,把要完成的任务写清楚。
复杂的算法。如果使用了独特的方法解决问题,要通过注释解释明白。这样不仅可以帮助别人查看代码,也可以帮助自己今后查看代码。
使用黑科技。由于浏览器之间的差异,JavaScript 代码中通常包含一些黑科技。不要假设其他人 一看就能明白某个黑科技是为了解决某个浏览器的什么问题。如果某个浏览器不能使用正常方 式达到目的,那要在注释里把黑科技的用途写出来。这样可以避免别人误以为黑科技没有用而 把它“修复”掉,结果你已解决的问题又会出现。 缩进和注释可以让代码更容易理解,将来也更容易维护。
变量和函数命名
以下是关于命名的通用规则。 变量名应该是名词,例如 car 或 person。
函数名应该以动词开始,例如 getName()。返回布尔值的函数通常以 is 开头,比如 isEnabled()。
对变量和函数都使用符合逻辑的名称,不用担心长度。长名字的问题可以通过后处理和压缩解 决(本章稍后会讨论)。
变量、函数和方法应该以小写字母开头,使用驼峰大小写(camelCase)形式,如 getName()和 isPerson。类名应该首字母大写,如 Person、RequestFactory。常量值应该全部大写并以 下划线相接,比如 REQUEST_TIMEOUT。
名称要尽量用描述性和直观的词汇,但不要过于冗长。getName()一看就知道会返回名称,而 PersonFactory 一看就知道会产生某个 Person 对象或实体。 要完全避免没有用的变量名,如不能表示所包含数据的类型的变量名。通过适当命名,代码读起来 就会像故事,因此更容易理解。
使用常量
关键在于把数据从使用它们的逻辑中分离出来。可以使用以下标准检查哪些数据需要提取。
重复出现的值:任何使用超过一次的值都应该提取到常量中,这样可以消除一个值改了而另一 个值没改造成的错误。这里也包括 CSS 的类名。
用户界面字符串:任何会显示给用户的字符串都应该提取出来,以方便实现国际化。
URL:Web 应用程序中资源的地址经常会发生变化,因此建议把所有 URL 集中放在一个地方 管理。
任何可能变化的值:任何时候,只要在代码中使用字面值,就问问自己这个值将来是否可能会 变。如果答案是“是”,那么就应该把它提取到常量中。
性能
随着作用域链中作用域数量的 增加,访问当前作用域外部变量所需的时间也会增加。访问全局变量始终比访问局部变量慢,因为必须 遍历作用域链。任何可以缩短遍历作用域链时间的举措都能提升代码性能。
避免全局查找
改进代码性能非常重要的一件事,可能就是要提防全局查询。全局变量和函数相比于局部值始终是 最费时间的,因为需要经历作用域链查找。来看下面的函数:
这个函数看起来好像没什么问题,但其中三个地方引用了全局 document 对象。如果页面的图片非 常多,那么 for 循环中就需要引用 document 几十甚至上百次,每次都要遍历一次作用域链。通过在局部作用域中保存 document 对象的引用,能够明显提升这个函数的性能,因为只需要作用域链查找。 通过创建一个指向 document 对象的局部变量,可以通过将全局查找的数量限制为一个来提高这个函数 的性能:
这里先把 document 对象保存在局部变量 doc 中。然后用 doc 替代了代码中所有的 document。 这样调用这个函数只会查找一次作用域链,相对上一个版本,肯定会快很多。因此,一个经验规则就是,只要函数中有引用超过两次的全局对象,就应该把这个对象保存为一个 局部变量。
选择正确的方法
常量值或 O(1),指字面量和保存在变量中的值,表示读取常量值所需的时间不会因值的多少而变化。 读取常量值是效率极高的操作,因此非常快。来看下面的例子:
以上代码查询了 4 次常量值:数值 5、变量 value、数值 10 和变量 sum。整体代码的复杂度可以认 为是 O(1)。 在 JavaScript 中访问数组元素也是 O(1)操作,与简单的变量查找一样。因此,下面的代码与前面的 例子效率一样:
使用变量和数组相比访问对象属性效率更高,访问对象属性的算法复杂度是 O(n)。访问对象的每个 属性都比访问变量或数组花费的时间长,因为查找属性名要搜索原型链。简单来说,查找的属性越多,执行时间就越长。来看下面的例子:
这个例子使用两次属性查找来计算 sum 的值。一两次属性查找可能不会有明显的性能问题,但几百 上千次则绝对会拖慢执行速度。
这里有 6 次属性查找:3 次是为查找 window.location.href.substring(),3 次是为查找 window.location.href.indexOf()。通过数代码中出现的点号数量,就可以知道有几次属性查找。 以上代码效率特别低,这是因为使用了两次 window.location.href,即同样的查找执行了两遍。 只要使用某个 object 属性超过一次,就应该将其保存在局部变量中。第一次仍然要用 O(n)的复杂 度去访问这个属性,但后续每次访问就都是 O(1),这样就是质的提升了。例如,前面的代码可以重写为 如下:
这个版本的代码只有 4 次属性查找,比之前节省了约 33%。在大型脚本中如果能这样优化,可能就 会明显改进性能。 通常,只要能够降低算法复杂度,就应该尽量通过在局部变量中保存值来替代属性查找。另外,如 果实现某个需求既可以使用数组的数值索引,又可以使用命名属性(比如 NodeList 对象),那就都应该使用数值索引。
switch 语句很快。如果代码中有复杂的 if-else 语句,将其转换成 switch 语句可以变得更快。然后,通过重新组织分支,把最可能的放前面,不太可能的放后面,可以进一步提升性能。
Last updated