前端开发 JS 15 最佳实践

作者 柚爸

最佳实践

学习完了JS基础语法,BOM与DOM对象,事件等一系列API。还有两部分高级函数应用和存储,遇到的时候再学习吧。

归根结底JS也是一种编程语言,一般到了最后,还是要提一下这门编程语言在开发时候的理念,高程三也不例外。这里列出这些“JavaScript之禅”:

  • 可维护性

    • 可理解性–代码可读性高,易于理解
    • 直观性–代码中的东西和体现的意图一看就能明白,不管操作过程有多复杂
    • 可适应性–代码以一种数据上的变化不要求完全重写的方法撰写
    • 可扩展性–在架构上已经考虑到在未来允许扩展
    • 可调试性–有地方出错的时候给予足够信息
  • 代码规范

    • 可读性
      • 统一缩进,一般为4个空格
      • 自定义函数和方法一定要有注释,描述目的和为了完成目的所使用的方法
      • 大段代码之前要有注释
      • 复杂的算法要有注释
      • 用特别的方法实现内容,一定要用注释
    • 变量和函数命名
      • 变量名应该为小写的名词,采用驼峰命名法
      • 函数和方法名应该以动词开始,动名词词组表示进行的动作,采用驼峰命名法。返回布尔值的函数要以is开头
      • 变量和函数在编写代码的时候不需要考虑长度,而要尽可能的表达意思。实际发布的时候可以压缩代码
    • 变量类型透明
      • 变量初始化之后就立刻赋值一个值用来表示变量类型
      • 使用匈牙利标记法指定变量类型
      • 使用行内注释写明该变量的类型
  • 降低耦合程度

    • 解耦HTML/JavaScript
      • HTML标签里不写事件属性标签
      • script标签里不使用内联代码
      • 避免使用innerHTML修改页面元素或使用JS代码
    • 解耦CSS/JavaScript
      • 不直接使用JS修改元素的.style.attr属性
      • 采用更改样式类的方式修改CSS样式
      • 不要为了方便JS操作特意设置内联样式
      • 考虑使用第三方样式库
    • 解耦应用逻辑/事件处理程序
      • 事件处理程序和应用逻辑程序相分离,不要在绑定事件的时候把业务逻辑一并写在事件处理程序中。
      • 事件处理程序用于捕获不同的事件调用应用逻辑函数,把事件对应的结果传进去
      • event对象仅在事件处理程序中使用,传递给应用逻辑函数的值一定是event对象中所需的数据。
      • 应用逻辑是否独立的标准是该应用逻辑函数不依赖事件处理程序可以独立运行。
      • 事件处理程序一定要处理事件,否则就不要进行绑定。
  • 编程实践

    • 尊重对象所有权
      • 不要为实例或原型添加属性或方法,原生对象更是如此
      • 不要重定义已经存在的方法
      • 不要覆盖任何已经存在的变量,即使是在代码块内也是这样。
      • 需要使用新类型就手工创建
    • 避免全局变量
      • 最多创建一个全局变量对象,把其他全局变量和对象保存在其中
      • 避免反复使用全局变量从而每次都查找完整条作用域链
    • 避免与null比较
      • 引用类型改用instanceof检查其构造函数
      • 基本类型使用typeof 操作符
      • 希望对象包含某个方法也用typeof 检查对象的方法名
    • 使用常量
      • 多次重复的值应该定义成常量
      • 显示给用户的字符串应该抽取成常量而不写死在程序里
      • Web应用中所有涉及URL的部分需要抽取出来统一存放和管理
      • 任意字面量如果未来还用到,就抽取成常量
      • ES6有了const 关键字用于定义常量
  • 性能

    • 注意作用域
      • 避免反复全局查找
      • 把反复查找的全局对象保存为变量
      • 不要使用with语句
    • 选择正确方法
      • 一旦有反复使用的对象,就放到局部变量中,第一次获取变量复杂度是O(n),之后就是O(1)
      • 数组优先用下标访问,不要使用名字访问。
    • 优化循环
      • 如果循环变量每次都要与一个特定值进行比较,用减值迭代替换加值迭代,可以只查询一次特定值
      • 简化终止条件
      • 简化循环体,密集计算不宜放在循环体内,可以从循环体获得变量后在循环体外单独计算。
      • 合理的情况下使用do-while,可以少测试一次循环体。但会带来易读性的问题。
    • 展开循环
      • 循环次数如果确定而且数据量很大的情况下,展开循环可能更快,即将循环分成固定长度的小段进行计算,减少了循环的上下文开销。
    • 避免双重解释
      • 尽量减少使用需要二次解释的语句,比如eval()和setTimeout中以字符串形式存在的JS语句,会导致再启动一个解释器进行解释。
      • 始终使用用函数替代需要解析的JS字符串。
    • 其他优化
      • 使用原生方法和对象速度较快
      • Swtich语句比多重判断要快
      • 位运算比布尔运算和算术运算要快,取模,逻辑与和或都可以用位运算替代
    • 最小化语句数
      • 用一个let 加逗号隔开的语句一次声明多个变量
      • 使用迭代值的时候合并语句,比如let name = values[i++]
      • 单纯创建数组和对象,使用字面量比构造函数要快得多。
  • 优化DOM交互

    • 最小化现场更新
      • 只更新需要更新的部分,而不是一起更新
      • 避免反复更新,将需要更新的内容一次性全部插入文档
      • innerHTML的性能比创建DOM元素再插入的性能要高,然而不适合太复杂的内容,会降低可读性。
      • 采用事件委托的形式处理事件,尽量减少页面中总的事件数量。
      • 减少访问HTMLCollection对象,这个访问开销非常大。

部署代码到生产环境

在完成了所有的代码编写后,最后一步就是部署到生产环境。但是在部署之前,还必须考虑到如下事项,最终会导致并不是直接将代码部署到生产环境中。

构建过程

如果说已经编写好的代码是A,而最终部署的代码是B的话,从A到B实际上就是将A构建为B的过程。这个过程要考虑如下的问题:

  • 知识产权问题,带有完整注释的代码放到网上,很容易被剽窃和找到漏洞
  • 给人阅读的代码除了注释以外,还包括不必要的换行和空格以及长的变量名,尤其是局部变量。对于解释器来说这些都不需要。
  • 可维护性高的源代码的排列并非是交给解释器的最佳方案。

在实践中,外部引用的文件越多,性能越低,加上上边的三个问题,一般采用一些第三方工具来重新组织代码,比如Ant,了解即可。

验证

著名的JSLint工具

常被用来做语法检查,现代的各种IDE也可以进行语法检查。

压缩

压缩分为对JS代码的压缩和对HTTP请求的压缩。

JS的压缩一般就是之前说的对于机器来讲不重要的部分都被删除,JS常用的压缩工具有很多,上传JS代码后即可得到压缩过的文件。

服务器和浏览器都支持压缩的情况下,在服务端开启压缩功能,用HTTP头部附加特别的信息来压缩。不过目前在高带宽的情况下,压缩使用的不是非常广泛。