三、函数

可读性:★★★★✰ 理解难度:★★★★✰

概述

如果把代码比喻成文章,那么类就是名词,函数就是动词,好的函数定义可以让程序变得生动而清晰,帮助你讲好这个故事。那么如何编写好函数呢?

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,长期有效

上一篇:《二、有意义的命名》

下一篇:四、注释

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享