可读性:★★★★✰ 理解难度:★★★★✰
概述
如果把代码比喻成文章,那么类就是名词,函数就是动词,好的函数定义可以让程序变得生动而清晰,帮助你讲好这个故事。那么如何编写好函数呢?
1.短小
在20世纪80年代,我们常说函数不该长于一屏。说这话的时候,VT100屏幕只有24行、80列。在JAVA面向对象的语言中,好的函数通常只有3、4行。对于JS 的Class写法和TypeScript同样可以做到。我们能做的就是尽量简短,保证一个函数只说一件事。
代码块和缩进
一般代码缩进最多不超过2层。
bad
// 像这样多层的判断缩进,一旦填充上复杂代码,将显得十分冗长,难于阅读
if () {
if () {
for () {
if ()
}
}
}
复制代码
good
// 可以把多层级进行拆解
if () {
doSmthing()
} else {
doElseThing()
}
复制代码
2.只做一件事
函数应该做一件事,做好这件事,只做一件事。(重要事情说3遍)
要判断函数是否不止做了一件事,还有一个方法,就是看它能否再拆出一个函数,该函数不仅只是单纯的重新诠释其实现。
比如,你的函数中包含以下动作:
(1)构建dom节点
(2)查找目标容器
(3)渲染HTML
(4)处理成功回调事件
那么就可以考虑拆分成多个函数了。
good
function() {
// 函数1
const createdDom = generateDom()
// 函数2
const targetContainer = findContainerByClause()
// 函数3
targetContainer.insert(creataedDom).then(() => {
// 函数4
successCallback()
})
}
复制代码
3.每个函数一个抽象层
意思是:函数中完成的内容应该是同一层次的抽象概念。
比如,同一个函数中如果存在以下动作,就不属于一个层次:
1)渲染HTML整个页面
2)生成HTML头部节点
2)新增一个DOM元素
bad
// 三个函数在抽象概念上不是一个层次的(渲染页面、创建页面头部、增加一个div)
function() {
renderPage()
createHeaderHtml()
header.appendNode('<div>text</div>')
}
复制代码
good
function() {
renderPage()
}
renderPage() {
createHeaderHtml()
createFooterHtml()
}
createHeaderHtml() {
header.appendNode('<div>text</div>')
header.appendNode('<div><button></div>')
}
复制代码
向下规则
函数的抽象层级自上而下。
比如:组装汽车 > 组装引擎 > 组装螺丝
4.使用具有描述性的名称
别害怕长名称。长而具有描述性的名称,要比短而令人费解的名称好。
风格尽量保持一致。
比如:
创建节点使用createHeader、createFooter这样统一的前缀规则。
不要有的是createHeader,有的是insertFooter。
5.函数参数
尽量少的参数
参数个数:0 > 1 > 2 > 3+
如果参数过多,就应该考虑封装对象或者提升成公有变量
bad
makeCircle(x, y, radius, color, borderColor)
复制代码
good
Point = {
x, y, color, borderColor
}
makeCircle(Point center, radius)
复制代码
标识型参数
标识型参数丑陋不堪,相当于大声宣布本函数不止做一件事。
bad
function() {
const isVueTemplate = getTempalteType()
render(isVueTemplate)
}
复制代码
good
function() {
const isVueTemplate = getTempalteType()
if (isVueTemplate) {
renderVue()
} else {
renderReact()
}
}
复制代码
6.无副作用
未预期的修改
bad
// 验证用户输入
validateInput(password, username) {
const valid = checkRegax(password, username)
// 这里的重置表单的动作,就不属于验证用户输入的范畴
if (!valid) {
resetForm()
}
return valid
}
复制代码
输出参数
参数多数会被自然而然地看作是函数的输入项,如果你的参数作为输出而非输入,多少会让人迷惑。
如下setUserIndex
方法就是这种情况
function() {
const userList = getUserList()
setUserIndex(userList)
userList...
}
setUserIndex(userList) {
userList.map((item, index) => {
item.index += 1
return item
})
}
复制代码
7.区分指令和判断
把指令和判断揉到一个函数中,可能会让人迷惑。
比如下面这个函数是为了:1. 设置用户的姓名,2. 判断用户是否已经存在
bad
// 这个if不通过的原因(设置姓名失败的原因),多少会让人有些疑惑
if (setUsername('wang')) {}
复制代码
good
if (nameNotExists('wang')) {
setUsername('wang')
}
复制代码
8.使用异常代替错误代码
为了是代码结构更加清晰易懂,可以用异常来代替各种错误代码的处理。ajax或者axios就是很好的例子。
ajax.post.then(() => {
}).catch((err) => {
})
复制代码
9.别重复自己
如果一个函数中的某些内容被重复多次,就可以考虑提取它了。
10.如何优化函数
从一开始就按照规则写函数,我想没人能做到。那么如何优化呢?
配上覆盖每行代码的单元测试,然后打磨这些代码,分解函数、修改名称、消除重复。
本文参考《代码整洁之道》(Robert C. Martin著,韩磊译)。
浙江大华技术股份有限公司-软研-智慧城市产品研发部招聘高级前端,欢迎来撩,有意向可发送简历到chen_zhen@dahuatech.com,长期有效
上一篇:《二、有意义的命名》
下一篇:四、注释