萝三画室

深入理解JS-part8-编程风格

本文是《编写可维护的javascript》的读书笔记,主要讲的是如何编写易读、可维护的JS代码。作为一枚合格的程序猿,是要对自己写的代码负责任的。在实际工作中,我们很少自己从头写代码,通常更多的是维护别人的代码。如果遇见风格糟糕的代码,我们就会被搞得对着代码一头雾水,针对是一项糟糕至极的体验。己所不欲勿施于人,那么我们就需要的一项素质,就是让自己写的代码,别人不需要花很大力气去理解,不需要花很大力气去维护。保证我们的代码,可以轻松的为人所用。那么开始吧~我们的目标是:没有蛀牙(误

基本格式化

本节讲的基本格式化包含一些细碎的,与语法无关的风格。适当的运用会大大增加代码的可读性。

  1. 空行
    空行会使代码更可读。那么什么时候更适合用空行呢?
    • 每个流控制语句之前(for,if等)
    • 方法之间
    • 方法中的局部变量和第一条语句之间
    • 单行或多行注释之前
    • 方法内的逻辑片段之间
  2. 命名
    驼峰式命名法是最常用的命名风格。
    • 变量命名,前缀应该是名次。如:name, age
    • 函数命名,前缀应是动词。如:getName, setAge, isTrue
    • 常量命名,使用大写字母和下划线命名,诶:MAX_COUNT
    • 构造函数命名,首字母大写
    • 命名长度尽可能短,内容突出重点,避免使用无意义的命名
  3. null&undefined
    null代表一个空的对象,我们应该在下列场景中使用null
    • 初始化一个变量,这个变量以后可能赋值为一个对象
    • 和一个已经初始化的变量比较
    • 当函数的参数期望是对象时,作为参数传入
    • 当函数的返回值期望是对象时,所谓返回值传出
      下列场景中避免使用null
    • 检测是否传入了某个参数
    • 检测一个未初始化的变量
      简而言之,我们把null理解为一个对象,它应该作为对象的占位符使用
      undefined代表未赋值,它的意思是这个对象已声明,但未赋值。
  4. 注释
    何时使用注释?添加注释的一般原则是,让代码含义变得更清晰
    • 难以理解的代码
    • 可能被任务是错误的代码
    • hack代码
  5. 语句和表达式

    • 所有块语句都要使用花括号不能省略,推荐的花括号对齐方式:左花括号置于块语句第一句的末尾
    • 块语句首行间隔推荐在圆括号左右各加一个空格
    • for in循环内必须使用hasownproperty检查实例属性
      好的栗子:
      1
      2
      3
      4
      5
      6
      for (var prop in obj) {
      if (object.hasOwnProperty(prop)) {
      console.log(prop);
      }
      }
  6. 变量、函数和运算符

  • 将局部变量的定义作为函数内的第一条语句
  • 将所有声明语句合并为一个语句,每个变量的初始化独占一行
  • 函数调用时,函数名和左括号之间没有空格
  • 立即调用函数的写法:(function(){}());
  • 判断相等时,推荐始终用===!==
  • 对于基本包装类型,推荐用字面量形式创建,不推荐用new的形式创建

UI层的松散耦合

对于Web页面,HTML、CSS和JS组成了UI。如果他们之间耦合太紧,那么修改一个的逻辑,也必须修改另一个的逻辑。松耦合对于代码的可维护性至关重要。我们的目标就是对其中一个部分的修改不会经常性的影响其他部分。

  1. 将JS从CSS中抽离 — 避免在CSS中写JS表达式
  2. 将CSS从JS中抽离 — 不要通过JS直接改变元素样式(如el.style.color=”red”;),使用JS操作CSS类来改变元素样式(如:el.classListadd(“redColor”))
  3. 将JS从HTML中抽离 — 尽量使用外部文件,不使用内联代码,尽量不直接在元素上挂载事件处理器

避免使用全局变量

全局变量可能引起:命名冲突、难以测试、代码脆弱。那么如何避免使用全局变量?

  • 给一个未被var声明的变量赋值时,这个变量就会变为全局变量。因此,我们应该总是定义变量。对于意外的情况,可以借助代码风格检查工具来检查。
  • 只创建一个全局对象,其他的变量作为该全局变量的成员。在实际工作中,也可以每个JS文件创建一个全局变量,作为命名空间
  • ES6中的模块系统可以支持输入输出时重命名
  • 使用立即执行函数包裹变量

事件处理

  • 将应用逻辑和事件处理的代码拆分。事件处理只负责找到应用逻辑所需要的、目标元素的属性,应用逻辑制赋值处理操作。
  • 不要分发事件对象。也就是说,在事件处理代码中,只提取应用逻辑所需要的属性,不应该把整个目标元素全部暴露出去。

这样完成了事件和应用逻辑的解耦,应用逻辑甚至可以复用在其他场景中。

类型检测

我们经常会遇到变量类型检测或者判断其是否为空。常用的类型检测方法是typeof和instanceof,不同数据类型以及对应typeof和instanceof会有不同的表现,详见本系列文章part5。

  • undefined类型用typeof检测
  • null用===检测
  • Boolean、string、number类型,如果是通过字面量方式创建的,则用typeof检测;如果是通过new操作符创建的,则用instanceof检测
  • Object类型用instanceof检测;其常用子类型:
    1. function类型用typeof检测
    2. array用Array.isArray()方法检测
    3. error、date、RegExp,用instanceof检测,此处注意跨帧问题

当检测某个对象中是否存在某个属性时,使用in运算符或hasOwnProperty()方法。前者检测范围包括对象原型链,后者检测范围不包括原型链,仅判断是否是实例属性。

抽离配置数据

代码中经常会出现我们“写死”的静态值,比如URL、展示给用户的字符串、配置参数等等。这些数据可能重复出现在代码中,如果我们每次都直接将这些值赋值给变量,那么一旦发生变更,就会造成很大的修改量,还可能出现漏改情况。那么一个比较好的解决办法是:

将配置数据从代码中剥离,存放在单独的文件中,实际使用时,从外部引用。文件格式可以是js,也可以是json或jsonp。

抛出自定义错误

抛出自定义错误的核心思想就是:在编写代码的过程中,意识到可能发生的错误,然后将关于错误的更多信息暴露出来。我们的目的并不是防止错误,而是在错误发生时能更容易定位和调试。

  • 何时抛出错误?
    JS不存在类型检查和参数检查,但我们也无需对每个函数都实现类型检测。这种做法不切实际并且会造成资源浪费。我们要做的,就是在代码中找出最可能引发错误的那个部分,并用尽可能小的代价去识别它。
  • 通过继承Error类型来创建自定义错误类型,区分浏览器错误和自己抛出的错误

浏览器嗅探

通常有以下几种方法进行浏览器嗅探:

  • 用户代理检测: navigator.userAgent
  • 特性检测: 探测浏览器是否支持某个具体功能
  • 特性推断:根据一个特性的存在与否推断另一个特性的存在与否
  • 浏览器推断:根据某些特性推断浏览器类型

浏览器可能不断更新,或者出现新型浏览器。因此如果单纯使用用户代理检测,可能会带来很多次的代码更新,也可能由于未及时更新而导致糟糕的用户体验。用户代理检测最安全的用法是检测早期版本的浏览器,比如IE8-;或者针对特定版本的浏览器。

特性检测是更优雅的办法,他根据浏览器是否支持某种方法来区分之后的操作方式。

避免特性推断。事实上,不应该从一个特性推断出另一个特性是否存在。这种方法逻辑薄弱,不能适应浏览器的更新和推新。

避免浏览器推断。与特性推断一样,依然是逻辑和浏览器更新问题,维护性很差。

因此,在浏览器嗅探时,建议尽可能的使用特性检测,这是最安全的方法;如果行不通的时候,尝试使用用户代理。避免使用特性推断与浏览器推断。

总结

通读此书收获不小,它提到了许多编程过程中的细节,很多细节都可能影响调试和后期维护。实现功能不是难事,易于理解、调试、维护、扩展才是难哪~~由于JS的灵活性以及客户端的千差万别,大概JS会比其他语言更需要注意这些方面。那么,希望自己可以保持良好的风格和习惯,让代码更优雅。

——本节完——