彻底掌握Function (二)

148 阅读4分钟

前言

今天是 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);

这是模拟编辑个人资讯的表单,可以填上姓名、年龄、性别三个栏位,按下送出表单的时候,会做到以下三件事:

  1. 检核 必填栏位 (有在 彻底掌握 Object提到过)
  2. 将前端栏位转换成后端需要的栏位与格式
  3. 发送API request 到后端

只有三个栏位,其实算是一个相对简单的范例,但可以看到要送出表单时,submitForm多达 22 行 (而且 fetch 还被我偷懒简化过)。

如果同样的目的,我们将 submitForm 拆解,分别用以下的 function 来处理:

  1. validateFormData
  2. prepareSubmitData
  3. 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 的一大卖点,如果同样或相似的逻辑,出现两次就该考虑是否写成 function 了,出现三次就要写检讨报告了 (?)

因为 function 的可重用性,可以大幅减少不必要的重复 code,feature 修改时只要修改一个地方,不会漏掉;要做测试的时候,也能保证结果相同,同时也提升了可维护性。

重点来了,什么时候会需要放到 function 重用?

其实。。。单纯就是。。。出现太多次的时候 ((拖走

比方说常用来发送 API request 的 axios 或fetch,就非常适合包进 function 里面重用

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 命名成applebanana之类的名字,肯定也是看不懂的 (应该说更加不懂),因此好的命名绝对是非常加分的!

而 function 的命名,某种程度上算是一种团队风格,只要团队中成员都能够好读、读懂。唯一的衡量标准应该就是 predictable,容易预测、容易猜到这个 function 要做什么,就是好命名。

而我自己遵守的主要是以下几点:

✔ 驼峰式命名 (camelCase)

这点算是非常好理解也容易上手,驼峰式就像是骆驼的背一样,凹下去凸起来凹下去凸起来,小写大写小写大写,所以比起全小写还容易阅读。

若是遇到缩写,则可以考虑使用底线 (_),虽然没有很建议,但也是个办法。

所以大概是这样:

applePie
bananaFish(?)
toDoList
HTML_Parser

✔ 动词 + 名词 (+ 修饰词)

function 本身就是用来「执行」一些任务的,所以必然是动词开头,而后面接名词则构成一个基本的语句 (主词大概是 user 吧!)。如果 V. + N. 的组合还不够清楚,还可以加上一些修饰词:

validateFormData
getProductList
findUserById

结语

function 经过拆解之后,重新命名给予逻辑意义,虽然功能都一样,但是当程式规模愈大,就愈能看出这样做的好处,下次当你有以下的感觉,不妨好好考虑「拆解 + 命名」吧!

  1. 这段 code 你觉得有点难懂,想要给它命名比较好理解
  2. 这段 code 你觉得有点冗长,想要拆出去瘦个身
  3. 这段 code 你觉得逻辑相似,想要重用一次解决