什么是可维护性的代码
什么是可维护性的代码
今天我们不聊性能优化,只是从后期维护代码的角度谈谈如何优雅的书写代码
- 为什么需要些可维护性高的代码 ?
在开发的过程中,迭代和维护是再正常不过的操作了
那么就必然要阅读别人的代码
你有没有遇到过一些尴尬的事情:
1、看不懂别人的代码,不知从何下手
2、修改一个功能,得读两天代码,改完发现 bug 最少的时候是修改以前
3、只是修改了一行代码,发现控制台报错好几十个
...
如果代码的可维护性高了,那么可以避免很多这些问题
编写可维护性高的代码, 从我做起 ^_^
- 什么是可维护性高的代码 ?
容易理解: 不需要求助源代码书写人员,就能看得懂
符合常识: 代码书写的自然通透
容易适配: 当数据发生变化的时候,不至于完全重写
容易扩展: 对于核心功能有可扩展性(适当利用策略模式)
容易调试: 当出现问题的时候,能给出明确且详细的错误提示,可以直接定位问题源
从下面几点做起:
一、代码可读性
- 想要好维护, 那么第一任务就是你写的代码要让别人看得懂
- 因为我们的代码,当他不运行的时候,就是一个纯文本
- 想要让别人看得懂你写的一堆文本,那么就要从一切自定义的内容开始做起
二、代码缩进
- 能区分是论文还是代码的第一因素,也是最直观的因素就是代码缩进
- 代码没有缩进,或者随机缩进,那么和给你看一篇火星文论文没有区别
[code]for (var i = 0; i < 100; i++) { if (true) { function fn() { for (var j = 0; j < 100; j++) { } } for (var j = 0; j < 100; j++) { } } }
整整齐齐的就是看不懂
- 我们严格保持了代码缩进以后, 虽然代码意义不一定看得懂, 但是代码结构我能看得懂了
[code]for (var i = 0; i < 100; i++) { if (true) { function fn() { for (var j = 0; j < 100; j++) { } } for (var j = 0; j < 100; j++) { } } }
- 这个时候就可以尝试下改一改了
三、注释
在任何一个语言里面,都是有注释的
语言规范里定义注释,不是为了让你学了玩的,就是为了让你对代码进行一些标注的
大型代码块,和大量变量堆积的地方,都要有清楚的注释,用来表明这个代码块或者说这一堆变量是干什么用的,尤其是函数,尽量做到每一个函数的前面都有一个说明注释。
[code]/* * fn 获取范围之间随机整数的函数 * @param {Number} a 范围开始的数字 * @param {Number} b 范围结束的数字 * @return {Number} 范围内的随机整数 */ function fn(a, b) { ... }
- 每一个函数都应该有参数说明,是否有返回值,返回值是什么
- 因为这些内容在函数定义中是不能直观看到了,需要阅读代码才可以
- 当你写明了这些以后,阅读性就大大提高了
- 假设,你的函数块里面涉及到很复杂的算法,最好也是在说明注释里面标注出来
当你对于一些浏览器问题做出的修复,你使用了一些黑科技
- 那么你一定要把这些黑科技标注出来,避免别人修改你的代码的时候
- 觉得这些黑科技没有用,给你删掉了,导致你修改好的问题又重新出现了
四、变量和函数命名
变量的命名和函数的命名,是最能体现我们自定义的地方
对于每一个变量和函数的命名,我们都尽量准确的给到一个语义,不管你是使用 大驼峰 还是 小驼峰,都要保证看到名字就能知道这个变量或者函数的意义
从变量来说
1、尽量使用名词,而不是动词
比如:car / person / show / ...
2、常量来说,要使用大写字母来表示
比如:TEST / BROWSER / ...
3、区分全局和私有变量,函数内的私有变量我会以 _ 开头
比如: _this / ...
从函数来说
1、当函数返回布尔值的时候, 一般会以 is 开头
比如:isEnabled() / isSelected() / ...
2、获取类的函数一般以 get 开头
比如:getUserList() / getUserInfo() / ...
3、设置类的一般使用 set 开头
比如:setName() / setUserInfo() / ...
4、修改类的一般使用 update 开头
比如:updateName() / updatePrice() / ...
4、程序处理类函数使用 handler 结尾
比如:showEditHandler() / submitHandler() / ...
5、尽可能的通过名字描述清楚函数的作用,不用担心太长,因为后期打包工具会帮我们处理掉的
比如: getUserInfoById() / delGoodsParamsById() / ...
五、变量类型透明化
因为
JS是一个弱类型语言,在定义变量的时候,不会限制数据类型
但是我们在给变量赋值的时候,也要尽可能的做到数据类型统一
当你需要定义一些变量,在后期操作中进行赋值的时候
尽可能在定义的时候,给一个初始值表示一下你变量将来要存储的数据类型
比如:
var count = 0;
var name = '';
var boo = false;
var person = null;
var todoList = [ ];
如果你实在不想给一个初始值
也可以使用注释的形式表明一下你定义的变量, 将来存储的是什么类型的数据
var count /* Number */;
var name /* String */;
var boo /* Boolean */;
六、代码书写习惯
我们要保证一个良好的代码书写习惯
七、链式编程的习惯
我们来看一下下面这个代码
[code][ ... ].map(function () { // code ... }).filter(function () { // code ... }).reduce(function () { // code ... })
其实没啥问题, 而且也挺好的
更甚至当代码简单一些的时候有人把它写成一行
[code][ ... ].map(function () { ... }).filter(function () { ... }).reduce(function () { ... })
但是到了后期修改的时候,问题就会逐步显示,一旦修改了第一个,那么后面的都有可能会出现问题
而且当代码量过大的时候,很难保证你不修改串行了
- 我们可以把上面的代码换成下面的方式
[code][ ... ] .map(function () { // code ... }) .filter(function () { // code ... }) .reduce(function () { // code ... })
这样的话,看起来会舒服的多
而且可以利用编辑器的代码折叠,一个函数一个函数的来书写
八、书写运算符的习惯
很多人喜欢相对紧凑的书写结构
比如下面的代码
[code]if (year%4===0&&year%100!==0||year%400===0) { ... }
很简单的一个判断闰年的代码
但是当你的运算符很紧凑的时候,那么看起来就会比较费眼睛
相对来说,我更喜欢在运算符两边都加上空格
让结构相对松散一些,看起来可能也容易一些
我们也不用担心这些空格,后期处理都会帮我们处理掉的
[code]if ( year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) { ... }
还有一种写法
[code]if ( year % 4 === 0 && year % 100 !== 0 || year % 400 === 0 ) { ... }
这个适用于条件比较长的时候使用
看起来会更加清晰一些
九、函数调用传递参数
- 当调用一个函数,需要传递一个函数作为参数的时候
- 我们通常都会直接书写一个匿名函数或者箭头函数在参数位置
- 或者说传递一个复杂数据类型作为参数的时候,都会直接讲对应的数组或者对象写在参数位置
- 比如下面这段代码
[code]$.get('/xxx', { a: 100, b: 200 }, function (res) { // code ... }, 'json')
代码没有问题,但是一旦对象中数据过多
或者函数中代码过多的时候
后期看起来就会很复杂
我会建议把这些内容单独书写出来
[code]var params = { a: 100, b: 200 } function success(res) { // code ... } $.get('/xxx', params, success, 'json')
这样一来, 不管是修改, 还是增加一些内容, 都会比较方便了
十、功能性函数的单独封装
把我们自定义的一些功能性函数进行单独的封装,放在一个单独的 JS 文件中进行引入或者导入使用,其实就是模块化的概念
十一、松散耦合
对于比较难以阅读的代码来说,强耦合的代码是最难阅读的,
JS代码本身层面上的耦合我们就不说了,大家都应该了解面向对象编程和模块化编程
十二、HTML 和 JavaScript 的耦合
在前端开发中,我们经常会见到有些人写代码会把一些简单的事件直接写到
html结构上
[code]<button onclick="doSomething()" ></button>
从代码层面上来说完全没有问题
但是实际上,这个是 HTML 和 JavaScript 的强耦合现象
第一: 每次对于代码进行的修改,都要从 HTML 和 JavaScript 两个位置去进行修改
第二: 代码引入位置不可变,一定要保证在用户点击之前就已经有函数存在了,不然一定会报错的
比较好的方法就是进行 HTML
和 JavaScript
的分离
在.js文件中获取 DOM 元素
通过事件绑定的形式来完成操作
[code]var btn = document.querySelector('button') btn.addEventListener('click', doSomething)
还有一种情况更常见, 就是在
JS代码中为了渲染页面而进行字符串拼接
[code]container.innerHTML = ` <div> ... <p> ... </p> <span> ... </span> </div> `
这个代码也是完全没有问题的,而且大部分同学都会这样书写代码,因为省时省力
但是这样的情况,一旦渲染到页面上,出现样式问题需要调整的时候
我们在 HTML 结构中很难找到内容来修改,必须要到 JavaScript 代码里面去修改
如果我们的字符串拼接是在循环里面完成的话,那么有可能你添加一个或者删除一个标签的时候,导致整个页面崩溃
比较好的做法
使用一些第三方小脚本或者模板引擎来进行渲染:
比如:art-template / e.js / ...
真的需要这样渲染的时候,那么在原始html结构中以注释的形式留下一部分渲染内容
[code]<div class="container"> <!-- 商品详情信息渲染结构 <div> ... <p> ... </p> <span> ... </span> </div> --> </div>
- 当 HTML 和 JavaScript 解耦以后
- 可以大量节省我们的排错时间, 和错误的准确定位
十三、CSS 和 JavaScript 的耦合
在前端的开发中,使用
JS来操作一些元素的样式,是在常见不过的事情了
比如我们经常会写
[code]ele.style.color = 'red' ; ele.style.display = 'none' ;
这样书写代码其实没有大问题
对于渲染也不会造成很大的困扰
但是,一旦我们需要修改样式的时候,那么就比较麻烦了
因为有的样式可能需要在.css文件内修改,有的样式需要在.js文件内修改
- 比较好的做法是, 把我们需要修改的样式写成一个单独类名下
- 放在
.css
文件内 - 我们在代码里面通过操作元素的类名来进行修改
[code]ele.classList.add('active') ele.classList.remove('active')
这样做保证了样式和行为的分离,我们在调整页面样式的时候,不需要 JS,直接在 CSS 中修改就可以
十四、事件处理 和 应用逻辑 的耦合
在开发过程中, 我们经常要处理一些事件,并且在事件里面要进行一些逻辑的运算
比如:我们在点击登录的时候,要对用户填写的内容进行一个正则的验证,然后提交到服务器
[code]ele.addEventListener('submit', function () { let username = xxx.value let password = xxx.value // 正则验证 if ( ... ) { ... } if ( ... ) { ... } // 提交到服务器 var xhr = new XMLHttpRequest() xhr.open( ... ) xhr.send( ... ) xhr.onload = function () { ... } })
这是一段合法的代码
但是函数里面包含了太多的内容
有事件处理
有逻辑处理
有请求发送
这样就相当于在一个函数里面做了太多的事情
这个代码的逻辑运算还是比较少的,但是一旦逻辑运算多了以后,那么后期阅读的时候就很麻烦了
我们可以把里面的逻辑运算和请求发送都单独提取出来,变成下面这个形式:
[code]function validateValue(val) { // 正则验证 if ( ... ) { ... } if ( ... ) { ... } // 将验证结果返回 return true // or false } function sendAjax() { // 发送请求的业务逻辑 } ele.addEventListener('submit', function () { let username = xxx.value let password = xxx.value // 正则验证 if (!validateValue( xxx )) return // 提交到服务器 sendAjax() })
这样一来,只要我们给函数写好注释,那么后期的时候,哪里出现问题,我们可以快速准确的定位问题所在位置
十五、尊重对象所有权
- JavaScript 的动态天性决定了没有什么是不能修改的
- 从代码层面出发,我们可以修改任何内容,包括向 Object 的 prototype 上扩展一些方法,,向 Array 的 prototype 上扩展一些方法
- 但是在真实的企业级开发过程中,我们要绝对的尊重每一个对象的所有权
不要修改任何不属于你的代码,如果某一个对象不是由你负责创建或者维护,那么你也不要修改他的构造函数
在好久好久以前:
我接触过一个叫做prototype的第三方库
它里面向 document 对象上扩展了一个叫做getElementsByClassName的方法
是不是看起来很无聊,但是在没有getElementsByClassName的年代,确实很好用
并且,扩展的这个getElementsByClassName方法的返回值是一个Array并不是我们后来使用的NodeList
而且还在实例身上扩展了一个叫做each()的方法,专门用来遍历
我们用起来的时候就会很方便
[code]document.getElementsByClassName('item').each()
这个很好,而且对代码开发进行了简化
但是,一旦浏览器厂商开始支持这个方法了,那么你的方法就会出现问题了
后来,在所有浏览器厂商都支持了getElementsByClassName以后
当在使用这个方法的时候,因为和原生的重名了
会出现代码的大面积报错
这个就是尊重代码所有权
因为你不知道浏览器厂商什么时候会 告知 或 不告知 的更新一些内容,或者修改一些 API
所以,不要修改任何不属于你的内容
十六、尽量不声明全局变量
和尊重对象所有权有密切关系的就是尽可能少的声明全局变量
抛开变量污染的层面不说,我们的每一个全局变量其实都是在向 window 上添加成员
[code]var name = 'Jack' function getInfo() { ... }
这都是全局变量,用起来也没什么问题
但是也确实是在 window 上挂载了两个名字
我们在开发自己的代码的时候, 尽可能的在全局制作一个命名空间,然后把我们所有需要的内容全部放在里面
[code]var myApp = { name: 'jack', getInfo () { ... } }
这样一来, 我们只是向 window 上挂载了一个 myApp
剩下的所有东西都在我自己的命名空间里面
一旦出现问题,你能准确的知道是你自己定义的变量或者方法出现了问题,还是原生的变量或者方法出现了问题
这个也是前端从没有模块化到模块化开发的演变过程的原始阶段:
- 独立命名空间
- IIFE
- AMD / CMD
- CommonJS
- ES6 模块化
十七、习惯使用常量
我们在开发的过程中,经常要使用一些变量来操作某些内容
- 任何出现一次以上的内容,都应该提取出来变成一个常量的定义
- 任何一个需要显示给用户看到的文本内容,都应该提取出来变成一个常量
- 任何一个变量,在定义的时候都要考虑,将来会不会发生变化,如果不发生变化,那么就直接定义成常量
- 包括我们在操作一些类名的时候,应该把这些类名提取出来做成常量,然后统一操作
这样一来,我们可以避免因为不小心修改变量而导致出现的问题,也可以在代码的各个部分保证代码数据的统一,避免一个东西这里修改了,那里没有修改的问题
- 什么代码叫简洁~什么叫"可维护性,可读性,抽象 = 好!"
- 什么是可维护性的代码,这十六点得知道
- 代码可调式、可维护性
- h264参考代码中的cbp是什么意思
- flag标志什么?哦,它标志代码馊了——(一)
- 2013-10-31 《三天里什么都没干……总之把目前为止的代码发了吧……》
- javaの碎碎念no.1-关于代码格式分析,每条代码代表什么
- HTTP 400/401/403/404/500网页错误代码是什么意思
- 阅读优秀的开源软件代码能够带给我们什么?(转)
- 调皮的代码:打印什么
- 这句代码什么意思?img, div, input { behavior: url("iepngfix.htc") }
- 正则表达式 不管在什么语言的编程代码中,都可以看到正则表达式的身影。如果没有正则,jQuery也不可能用不到1万行能完成的。个人感觉正则还是得多练,多写。(当然前提是要对其规则有很好的理解) 我在开发
- HONGJIN2:大家讨论是什么力量驱使你写代码的!
- html是什么?html简单网页代码
- Phabricator是什么,代码审查工具
- 张孝祥老师交通灯管理系统的学习笔记 在做一件事时,首先要明确要达到什么效果。有目的性。就软件项目来说就是,首先要看的就是项目所提出的项目要求。做项目,不急于写代码,先把问题搞清楚,把要求分
- Android 命名规范 (提高代码可维护性)
- 以下代码的输出结果是什么?
- 用设计器设计一个表后,在自己的软件代码里用什么命令来修改SQL查询命令和参数呢?
- 如何提高代码质量,或者说高质量代码的特征是什么