本文是《编写可维护的javascript》的读书笔记,主要讲的是如何编写易读、可维护的JS代码。作为一枚合格的程序猿,是要对自己写的代码负责任的。在实际工作中,我们很少自己从头写代码,通常更多的是维护别人的代码。如果遇见风格糟糕的代码,我们就会被搞得对着代码一头雾水,针对是一项糟糕至极的体验。己所不欲勿施于人,那么我们就需要的一项素质,就是让自己写的代码,别人不需要花很大力气去理解,不需要花很大力气去维护。保证我们的代码,可以轻松的为人所用。那么开始吧~我们的目标是:没有蛀牙(误
基本格式化
本节讲的基本格式化包含一些细碎的,与语法无关的风格。适当的运用会大大增加代码的可读性。
- 空行
空行会使代码更可读。那么什么时候更适合用空行呢?- 每个流控制语句之前(for,if等)
- 方法之间
- 方法中的局部变量和第一条语句之间
- 单行或多行注释之前
- 方法内的逻辑片段之间
- 命名
驼峰式命名法是最常用的命名风格。- 变量命名,前缀应该是名次。如:name, age
- 函数命名,前缀应是动词。如:getName, setAge, isTrue
- 常量命名,使用大写字母和下划线命名,诶:MAX_COUNT
- 构造函数命名,首字母大写
- 命名长度尽可能短,内容突出重点,避免使用无意义的命名
- null&undefined
null代表一个空的对象,我们应该在下列场景中使用null- 初始化一个变量,这个变量以后可能赋值为一个对象
- 和一个已经初始化的变量比较
- 当函数的参数期望是对象时,作为参数传入
- 当函数的返回值期望是对象时,所谓返回值传出
下列场景中避免使用null - 检测是否传入了某个参数
- 检测一个未初始化的变量
简而言之,我们把null理解为一个对象,它应该作为对象的占位符使用
undefined代表未赋值,它的意思是这个对象已声明,但未赋值。
- 注释
何时使用注释?添加注释的一般原则是,让代码含义变得更清晰- 难以理解的代码
- 可能被任务是错误的代码
- hack代码
语句和表达式
- 所有块语句都要使用花括号不能省略,推荐的花括号对齐方式:左花括号置于块语句第一句的末尾
- 块语句首行间隔推荐在圆括号左右各加一个空格
- for in循环内必须使用hasownproperty检查实例属性
好的栗子:123456for (var prop in obj) {if (object.hasOwnProperty(prop)) {console.log(prop);}}
变量、函数和运算符
- 将局部变量的定义作为函数内的第一条语句
- 将所有声明语句合并为一个语句,每个变量的初始化独占一行
- 函数调用时,函数名和左括号之间没有空格
- 立即调用函数的写法:(function(){}());
- 判断相等时,推荐始终用
===
和!==
- 对于基本包装类型,推荐用字面量形式创建,不推荐用new的形式创建
UI层的松散耦合
对于Web页面,HTML、CSS和JS组成了UI。如果他们之间耦合太紧,那么修改一个的逻辑,也必须修改另一个的逻辑。松耦合对于代码的可维护性至关重要。我们的目标就是对其中一个部分的修改不会经常性的影响其他部分。
- 将JS从CSS中抽离 — 避免在CSS中写JS表达式
- 将CSS从JS中抽离 — 不要通过JS直接改变元素样式(如el.style.color=”red”;),使用JS操作CSS类来改变元素样式(如:el.classListadd(“redColor”))
- 将JS从HTML中抽离 — 尽量使用外部文件,不使用内联代码,尽量不直接在元素上挂载事件处理器
避免使用全局变量
全局变量可能引起:命名冲突、难以测试、代码脆弱。那么如何避免使用全局变量?
- 给一个未被var声明的变量赋值时,这个变量就会变为全局变量。因此,我们应该总是定义变量。对于意外的情况,可以借助代码风格检查工具来检查。
- 只创建一个全局对象,其他的变量作为该全局变量的成员。在实际工作中,也可以每个JS文件创建一个全局变量,作为命名空间
- ES6中的模块系统可以支持输入输出时重命名
- 使用立即执行函数包裹变量
事件处理
- 将应用逻辑和事件处理的代码拆分。事件处理只负责找到应用逻辑所需要的、目标元素的属性,应用逻辑制赋值处理操作。
- 不要分发事件对象。也就是说,在事件处理代码中,只提取应用逻辑所需要的属性,不应该把整个目标元素全部暴露出去。
这样完成了事件和应用逻辑的解耦,应用逻辑甚至可以复用在其他场景中。
类型检测
我们经常会遇到变量类型检测或者判断其是否为空。常用的类型检测方法是typeof和instanceof,不同数据类型以及对应typeof和instanceof会有不同的表现,详见本系列文章part5。
- undefined类型用typeof检测
- null用===检测
- Boolean、string、number类型,如果是通过字面量方式创建的,则用typeof检测;如果是通过new操作符创建的,则用instanceof检测
- Object类型用instanceof检测;其常用子类型:
- function类型用typeof检测
- array用Array.isArray()方法检测
- error、date、RegExp,用instanceof检测,此处注意跨帧问题
当检测某个对象中是否存在某个属性时,使用in运算符或hasOwnProperty()方法。前者检测范围包括对象原型链,后者检测范围不包括原型链,仅判断是否是实例属性。
抽离配置数据
代码中经常会出现我们“写死”的静态值,比如URL、展示给用户的字符串、配置参数等等。这些数据可能重复出现在代码中,如果我们每次都直接将这些值赋值给变量,那么一旦发生变更,就会造成很大的修改量,还可能出现漏改情况。那么一个比较好的解决办法是:
将配置数据从代码中剥离,存放在单独的文件中,实际使用时,从外部引用。文件格式可以是js,也可以是json或jsonp。
抛出自定义错误
抛出自定义错误的核心思想就是:在编写代码的过程中,意识到可能发生的错误,然后将关于错误的更多信息暴露出来。我们的目的并不是防止错误,而是在错误发生时能更容易定位和调试。
- 何时抛出错误?
JS不存在类型检查和参数检查,但我们也无需对每个函数都实现类型检测。这种做法不切实际并且会造成资源浪费。我们要做的,就是在代码中找出最可能引发错误的那个部分,并用尽可能小的代价去识别它。 - 通过继承Error类型来创建自定义错误类型,区分浏览器错误和自己抛出的错误
浏览器嗅探
通常有以下几种方法进行浏览器嗅探:
- 用户代理检测: navigator.userAgent
- 特性检测: 探测浏览器是否支持某个具体功能
- 特性推断:根据一个特性的存在与否推断另一个特性的存在与否
- 浏览器推断:根据某些特性推断浏览器类型
浏览器可能不断更新,或者出现新型浏览器。因此如果单纯使用用户代理检测,可能会带来很多次的代码更新,也可能由于未及时更新而导致糟糕的用户体验。用户代理检测最安全的用法是检测早期版本的浏览器,比如IE8-;或者针对特定版本的浏览器。
特性检测是更优雅的办法,他根据浏览器是否支持某种方法来区分之后的操作方式。
避免特性推断。事实上,不应该从一个特性推断出另一个特性是否存在。这种方法逻辑薄弱,不能适应浏览器的更新和推新。
避免浏览器推断。与特性推断一样,依然是逻辑和浏览器更新问题,维护性很差。
因此,在浏览器嗅探时,建议尽可能的使用特性检测,这是最安全的方法;如果行不通的时候,尝试使用用户代理。避免使用特性推断与浏览器推断。
总结
通读此书收获不小,它提到了许多编程过程中的细节,很多细节都可能影响调试和后期维护。实现功能不是难事,易于理解、调试、维护、扩展才是难哪~~由于JS的灵活性以及客户端的千差万别,大概JS会比其他语言更需要注意这些方面。那么,希望自己可以保持良好的风格和习惯,让代码更优雅。
——本节完——