今天是 function 时空旅行第二天,昨天我们学会了如何收拾背包(?),也学会不要让背包里的东西变质(?)。。。总之就是学会了关於 function 参数的另一种使用方式,提高可维护性。
今天要来谈谈关於 function 的拆解与命名:
跟昨天一样,先来个范例看看吧:
// 送出编辑个人资讯的表单
/*
* name : 姓名
* age : 年龄
* gender: 性别
*/
const requiredFields = [
'name',
'age',
'gender'
];
const submitForm = (value) => {
const formFields = Object.keys(value);
const valid = requiredFields.every(key => {
return formFields.includes(key) && typeof value[key] !== 'undefined'
});
if(!valid) {
console.log('尚有必填栏位未填');
return;
}
const nameSplitList = value.name.split(' ');
const submitValue = {
firstName: nameSplitList[0],
lastName: nameSplitList[1],
age: Number(value.age),
gender: value.gender
};
fetch('my-backend-API', {
method: 'POST',
body: JSON.stringify(submitValue)
});
};
const formValue = {
name: 'yc chiu',
age: '20',
gender: 'male'
};
submitForm(formValue);
这是模拟编辑个人资讯的表单,可以填上姓名、年龄、性别三个栏位,按下送出表单的时候,会做到以下三件事:
只有三个栏位,其实算是一个相对简单的范例,但可以看到要送出表单时,submitForm
多达 22 行(而且 fetch
还被我偷懒简化过)。
如果同样的目的,我们将 submitForm
拆解,分别用以下的 function 来处理:
validateFormData
prepareSubmitData
postPersonData
// 送出编辑个人资讯的表单
/*
* name : 姓名
* age : 年龄
* gender: 性别
*/
const requiredFields = [
'name',
'age',
'gender'
];
const validateFormData = (formData) => {
const formFields = Object.keys(formData);
return requiredFields.every(key => {
return formFields.includes(key) && typeof formData[key] !== 'undefined'
});
};
const prepareSubmitData = (formData) => {
const nameSplitList = formData.name.split(' ');
return {
firstName: nameSplitList[0],
lastName: nameSplitList[1],
age: Number(formData.age),
gender: formData.gender
};
};
const postPersonData = (submitData) => {
fetch('my-backend-API', {
method: 'POST',
body: JSON.stringify(submitData)
});
};
const submitForm = (value) => {
const valid = validateFormData(value);
if(!valid) {
console.log('尚有必填栏位未填');
return ;
}
const submitData = prepareSubmitData(value);
postPersonData(submitData);
};
const formValue = {
name: 'yc chiu',
age: '20',
gender: 'male'
};
submitForm(formValue);
没错,改完之後程序码更长了(傻眼),但我们换到什麽呢?
虽然程序码更多了,但对於刚接手这份 code 的人来说,其实读懂的速度更快了。
重点在於我们将 submitForm
这个 function 里面,原本混杂没有界线的逻辑,使用几个小 function 切割开来,强迫这些逻辑拆散,就不再是一大坨程序要一行一行读。
不过要强调的是,增加可读性不是一定要拆 function 才做得到,比较懒人一点做法,可以加上注解:
const submitForm = (value) => {
// 检核必填栏位
const formFields = Object.keys(value);
const valid = requiredFields.every(key => {
return formFields.includes(key) && typeof value[key] !== 'undefined'
});
if(!valid) {
console.log('尚有必填栏位未填');
return;
}
// 将栏位值转换为後端需要的栏位与格式
const nameSplitList = value.name.split(' ');
const submitValue = {
firstName: nameSplitList[0],
lastName: nameSplitList[1],
age: Number(value.age),
gender: value.gender
};
// 发送 API request 到後端
fetch('my-backend-API', {
method: 'POST',
body: JSON.stringify(submitValue)
});
};
这是用注解也办不到的事,是 function 的一大卖点,如果同样或相似的逻辑,出现两次就该考虑是否写成 function 了,出现三次就要写检讨报告了(?)
因为 function 的可重用性,可以大幅减少不必要的重复 code,feature 修改时只要修改一个地方,不会漏掉;要做测试的时候,也能保证结果相同,同时也提升了可维护性。
重点来了,什麽时候会需要放到 function 重用?
其实。。。单纯就是。。。出现太多次的时候((拖走
比方说常用来发送 API request 的 axios
或 fetch
,就非常适合包进 function 里面重用(部分大写变数,不是本次重点,可自行体会XD):
const callApi = async(endpoint, method = 'get', body) => {
const requestUrl = `${API_URL}/${endpoint}`;
const options = {
headers: {
'content-type': 'application/json',
Authorization: TOKEN,
},
method
};
if (body) {
options.body = JSON.stringify(body);
}
try {
const response = await fetch(requestUrl, options);
if (response.ok) {
const json = await response.json();
return json;
}
return Promise.reject(response);
} catch (error) {
throw new Error(error);
}
};
呼叫时都只要一行
// GET
const productList = await callApi(`/product`);
// POST
const productResult = await callApi(`/product`, 'post', data);
当然,光是拆散还不够,如果把这些小 function 命名成 apple
、banana
之类的名字,肯定也是看不懂的(应该说更加不懂),因此好的命名绝对是非常加分的!
而 function 的命名,某种程度上算是一种团队风格,只要团队中成员都能够好读、读懂。唯一的衡量标准应该就是 predictable,容易预测、容易猜到这个 function 要做什麽,就是好命名。
而我自己遵守的主要是以下几点:
这点算是非常好理解也容易上手,驼峰式就像是骆驼的背一样,凹下去凸起来凹下去凸起来,小写大写小写大写,所以比起全小写还容易阅读。
其实照这样说,手握拳的时候,手指根部的四个关节也是凹下去凸起来啊,怎麽不叫指关节式命名(?),是因为多一个字吗(?)
若是遇到缩写,则可以考虑使用底线(_),虽然没有很建议,但也是个办法。
所以大概是这样:
applePie
bananaFish(?)
toDoList
HTML_Parser
function 本身就是用来「执行」一些任务的,所以必然是动词开头,而後面接名词则构成一个基本的语句(主词大概是 user 吧!)。如果 V. + N. 的组合还不够清楚,还可以加上一些修饰词:
validateFormData
getProductList
findUserById
确保每个动词都意义一致,尤其是一些翻成中文相似的动词:
patch 用於部分更新
put 用於替换
fetch 用於发送 request
get 取得的万用字(?)
function 经过拆解之後,重新命名给予逻辑意义,虽然功能都一样,但是当程序规模愈大,就愈能看出这样做的好处,下次当你有以下的感觉,不妨好好考虑「拆解+命名」吧!
离开了熟悉的家乡
改名换姓
在银河的另一端相遇
<<: Day 8 - 使用 Order API 建立测试订单
URL : https://app.hackthebox.eu/machines/14 IP : ...
Abstract 小编先前每个范例都有提供服务(Service)层级的测试案例,但部分开发者会开发许...
题干懒人包 输入一个数组及一个数,最後输出一个数值代表非重复数值的数量,然後以下几点要注意: 只能修...
身为一个App的开发新手常常会遇到一些莫名其妙又难以解决的问题,直到找到问题答案才发现根本是自己愚蠢...
我们可运用「目标客户比对」来增加曝光机会: 向现有客户交叉销售他们可能喜欢的其他产品或服务 找出与理...