提炼函数
提炼函数作为代码重构中常见的方式之一,是应该最被重视的。但我们程序中某些片段可以被独立出来,那么就建议将这部分独立为一个函数,通过命名以及解耦的方式这端代码发挥更大的价值。最被提倡的函数是纯函数。
提炼函数的主要优点:
- 避免出现超大函数
- 有利用代码复用
- 有利于问题排查
- 有利于函数的覆盖
- 具有良好命名的函数容易被解读,能起到较好的注释作用
举例:拼接用户信息
async getUser = function() {
var user = await Api.getUser() ;
var {name, age, sex} = user ;
var userInfo = `${name}:${age}岁,性别:${sex}`;
}
// 提炼函数 ,提炼之后的好处是与拼接用户信息的需求都独立为函数,函数对外只依赖user对象
async getUser = function() {
var user = await Api.getUser() ;
var userInfo = gerUserInfoMsg(user);
}
function gerUserInfoMsg (user){
var {name, age, sex} = user ;
return `${name}:${age}岁,性别:${sex}`;
}
合并重复的代码段
如果一个代码段有大量的重复代码,尤其常见的是分支语句中有重复代码,有必要进行合并去重。
比如下面的案例:重复调用setState有一定规律的属性赋值,重复的判断一些type类型都可以进行合并去重。
setDropDownVisible = (type) => {
if(type === 'vaild'){
this.setState({
vaildDropdown: true
})
}
if(type === 'sex'){
this.setState({
sexDropdown: true
})
}
if(type === 'age'){
this.setState({
ageDropdown: true
})
}
}
// 优化后
setDropDownVisible = (type) => {
const typeArr = ['vaild', 'sex', 'age'];
if(typeArr.includes(type)){
this.setState({
[`${type}Dropdown`]: true
})
return
}
console.warning(type + 'is not in stand type');
}
条件分支语句提炼为函数
在复杂度较高的程序中会存在很多看似很复杂的条件判断形成分支语句以及与这个条件有关的封装代码段,如果某个条件构成良好的准则,可以将判断条件进行封装,让逻辑更加清晰。
function getPrice(price,month){
if(!month) return ;
if(month >= 6 && month <= 9 ) {
return price * 0.8
}
}
// 其中 month >= 6 && month <= 9的代码含义是判断是否处于夏季,提炼函数
function isSummer(month){
return month >= 6 && month <= 9
}
function getPrice(price,month){
if(!month) return ;
if(isSummer(month)) {
return price * 0.8
}
}
合理使用循环
看似简单的循环可以帮我们处理很多重复而简单的代码以及过程,也可以让维护更加简单,让代码更有规律。
render(){
return (
<select>
<option value="1">第一季度</option>
<option value="2">第二季度</option>
<option value="3">第三季度</option>
<option value="4">第四季度</option>
</select>
)
}
// 上面的option明显的可以用循环的方式来进行
render(){
const seasonArr = [
{
value: 1,
label: '第一季度'
},
{
value: 2,
label: '第二季度'
},
{
value: 3,
label: '第三季度'
},
{
value: 4,
label: '第四季度'
},
];
return (
<select>
{seasonArr.map(item) => (<option value={value} key={value}>{label}</option>)}
</select>
)
}
提前让函数退出
在我们书写大多数函数的时候,很多人喜欢用一个返回关键变量控制结果,然后最后返回结果。其实这样的书写可以简化为当得到结果时立即返回,一则逻辑更加清晰,减少了很多嵌套 ;二则 函数的运行效率更高。
function judgeAge(age){
var judgeResult = '';
if(age < 18){
judgeResult = '未成年'
} else if(age < 30){
judgeResult = '青年'
} else if(age < 50){
judgeResult = '中年'
} else if(age < 100){
judgeResult = '老年'
}
return judgeResult;
}
// 改造后,判定结果收集,符合任何一个条件,马上返回跳出函数
function judgeAge(age){
var judgeResultArr = ['未成年', '青年','中年', '老年'];
if(age < 18){
return judgeResultArr[0]
}
if(age < 30){
return judgeResultArr[1]
}
if(age < 50){
return judgeResultArr[2]
}
if(age < 100){
return judgeResultArr[3]
}
}
传递对象参数代替长参数列表
function person(age, sex, age, edu){
}
// 参数改造后
function person(personObj){
}
var personObj = {
name:"zhangsan",
sex: 'male',
age: 12,
}
var zhangsan = new Person(personObj);
分析函数特点,减少参数
需要将代码进行解析,是否有些参数是存在关联的,可代替的,比如我们需要画布设置一个正方形的div色块,需要传入宽高以及面积。
而经过分析之后,我们先确定需求,如果只需要画正方形,那么只需要传入宽度,高度等于宽度不用传,面积也可以通过计算得知。
function drawSquare(width, height, s){
}
function drawSquare(width){
let height = width;
let s = width * height;
}
避免使用过重的三目运算
三目运算可以解决的问题,需要返回一个结果值,并且没有过多的逻辑判断。如果你是需要逻辑与运算、或运算,可以直接采用熔断写法。
比如下面的,let sex = sex ? sex : 'male'
, 可以用let sex = sex || 'male'
,那么就可以 更简单;
同理,如果你希望基于存在的情况下去判断执行语句,否则不执行逻辑 ,
比如jsx语法中: {isAble ? <button/> : null}
,直接写成,{isAble && <button/>}
let patch = fs ? path ? fs(path) : fs('xxx') : null
// 这种多余两层的逻辑判断更建议大家写if/else 实现
if(fs) {
if(path){
} else {
}
} else {
}
合理使用链式调用
具体参照链的设计模式,做法很简单,在方法执行完成后返回this,就可以实现链式的不断调用执行方法,可以简化大量的代码。
分解大型类
主要是将复杂的方法或者算法或者具有典型特征的对象进行肢解,让其以组合的方式进行设计,不但进行了解耦,而且可以让整体的架构可维护性更强。
break 跳出多重循环
因为for循环,尤其多层嵌套循环会占用的大量的内存以及运行时间,如果在函数得到期望结果的时候,那么就建议及时的跳出循环,避免性能的浪费。比如我们要写一个Array.prototype.includes方法.
Array.prototype.includes = function (arr, key){
if(!Array.isArray(arr)) return false ;
let isInclude = false;
for(let i = 0;len = arr.length;i<len;i++){
if(key === arr[i]){
isInclude = true;
break;
}
}
return isInclude;
}