最近带项目组简单做了一下
code review,发现了组员很多地方,代码冗余,命名式编程太多,函数式编程几乎用的很少,于是我就开始思考如何帮助组员改善这下地方,下面我简单说说我发现的几个可以改善的地方
范围值对应单一值
示例描述:根据 文件后缀
fileSuffix所属的范围,获取对应单一的icon值
// 文件后缀
const fileSuffix = 'ppt'
// 创建映射
const map = {
"xls":["xls", "xlsx", "xlsm"],
"doc": ["doc", "docx"],
"ppt": ["ppt", "pptx"],
"pdf": ["pdf"],
"audio":["mp3", "wma"],
"media":["mp4"],
"zip": ["zip", "7z", "rar", "apz"],
"txt":["txt"],
"image":["jpg", "jpeg", "gif", "png"]
}
// 有的小伙伴可能或 for in 循环对象判断
// 少数或许实现思路是这样的
const icon = Object.keys(map)[Object.values(map).findIndex(i => i.includes(fileSuffix))] || 'default'
思考:如果我们现在有这么一个场景,多个范围值对应一个单一值,这个 单一值 可以是一个字符串,也可以是一个函数,传入的值如果不在对应范围值内,我们需要返回一个默认值或执行一个默认的函数,那么我们又当如何做呢
示例
// 文件后缀
const fileSuffix = 'ppt'
const defaultFn = ()=>{}
// 如果 fileSuffix 是属于 ["xls", "xlsx", "xlsm"]当中,返回"xls"
// 如果 fileSuffix 是属于 ["zip"] 当中,就返回 逻辑处理的的结果,否则就 执行默认函数
if(["xls", "xlsx", "xlsm"].includes(fileSuffix)){
return "xls"
}else if(["zip"].includes(fileSuffix)){
// 各种逻辑处理...
return "各种逻辑处理后的结果"
}
// 两个还好处理,一旦变得多起来,这种if else 就不好维护了
有的小伙伴或许想到了 Map,因为任何值(对象或者原始值) 都可以作为一个键,但是我还是不想用 Map 来实现,请原谅我的倔强
继续思考和优化,创建了3种数据类型,都需要达到兼容
// 文件后缀
const fileSuffix = 'ppt'
// 创建数 据类型 1,fileSuffix 只有属于对应 key 解析后的数组,就返回对应的字符串值
const type1 = {
'["xls", "xlsx", "xlsm"]':"xls",
'["doc", "docx"]':"doc",
'["ppt", "pptx"]':"ppt",
'["pdf"]':"pdf",
'["mp3", "wma"]':"audio",
'["mp4"]':"media",
'["zip", "7z", "rar", "apz"]':"zip",
'["txt"]':"txt",
'["jpg", "jpeg", "gif", "png"]':"image"
}
// 创建数 据类型 2,fileSuffix 只有属于对应 key 解析后的数组,就返回对应函数返回的值
const type2 = {
'["xls", "xlsx", "xlsm"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"xls"
},
'["doc", "docx"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"doc"
},
'["ppt", "pptx"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"ppt"
},
'["pdf"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"pdf"
},
'["mp3", "wma"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"audio"
},
'["mp4"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"media"
},
'["zip", "7z", "rar", "apz"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"zip"
},
'["txt"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"txt"
},
'["jpg", "jpeg", "gif", "png"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"image"
}
}
// 创建数 据类型 3,fileSuffix 只有属于对应 key 解析后的数组,就返回对应的字符串值 或 返回 对应函数返回的值
const type3 = {
'["xls", "xlsx", "xlsm"]':"xls",
'["doc", "docx"]':"doc",
'["ppt", "pptx"]':"ppt",
'["pdf"]':"pdf",
'["mp3", "wma"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"audio"
},
'["mp4"]':"media",
'["zip", "7z", "rar", "apz"]':"zip",
'["txt"]':"txt",
'["jpg", "jpeg", "gif", "png"]':()=>{
// 各种逻辑处理...
return "各种逻辑处理的结果"+"image"
}
}
事实上,实际项目中就有很多这样的需求,用 对象处理也是简化 if else的最佳方案,我们用一个函数来兼容这些场景,PS:我们map 对象的key种的属性值 只支持数组 和 字符串
/**
* @description: 简化函数 key为数组字符串
* @param { Object } o: 像 上面的 3种对象
* @param { String } s :像 上面的 3种对象一样的 key 字符串
* @param { String | Function } d :没有匹配到 给的默认值 或是 执行的默认函数
*/
function simplify(o, s, d){
// 查询是否能找到对应的key
const k = Object.keys(o).find(k => JSON.parse(k).includes(s))
// 如果查找到 并且 其对应的值是字符串,那么直接返回该字符串
if(typeof o[k] ==='string')return o[k]
// 如果查找到 并且 其对应的值是函数,那么直接执行
if(typeof o[k] === 'function')return o[k]()
// 如果没有查找到并且 默认d是字符串,那么就返回默认d值
if(!o[k] && d && typeof d ==='string')return d
// 如果没有查找到并且 默认d是函数,那么就直接执行
if(!o[k] && d && typeof d === 'function') return d()
}
// 如何使用
// 没有从type1 种匹配到 'wma',就返回 'default',匹配到就返回匹配值
simplify(type1,'wma','default')
simplify(type1,'wma1',()=>{
// 没有匹配到 执行该默认函数
console.log('没有匹配到 执行该默认函数了')
})
if
有这么段代码,代码如下
const a = 1
const b = 1
const c = 1
if (a === 1) {
if (b === 1) {
if (c === 1) {
return 'a1-b1-c1'
} else if (c === 2) {
return 'a1-b1-c2'
} else {
return 'a1-b1-else'
}
} else if (b === 2) {
if (c === 1) {
return 'a1-b2-c1'
} else if (c === 2) {
return 'a1-b2-c2'
} else {
return 'a1-b2-else'
}
} else {
if (c === 1) {
return 'a1-else-c1'
} else if (c === 2) {
return 'a1-else-c2'
} else {
return 'a1-else-else'
}
}
} else if (a === 2) {
if (b === 1) {
if (c === 1) {
return 'a2-b1-c1'
} else if (c === 2) {
return 'a2-b1-c2'
} else {
return 'a2-b1-else'
}
} else if (b === 2) {
if (c === 1) {
return 'a2-b2-c1'
} else if (c === 2) {
return 'a2-b2-c2'
} else {
return 'a2-b2-else'
}
} else {
if (c === 1) {
return 'a2-else-c1'
} else if (c === 2) {
return 'a2-else-c2'
} else {
return 'a2-else-else'
}
}
} else {
if (b === 1) {
if (c === 1) {
return 'else-b1-c1'
} else if (c === 2) {
return 'else-b1-c2'
} else {
return 'else-b1-else'
}
} else if (b === 2) {
if (c === 1) {
return 'else-b2-c1'
} else if (c === 2) {
return 'else-b2-c2'
} else {
return 'else-b2c-else'
}
} else {
if (c === 1) {
return 'else-else-c1'
} else if (c === 2) {
return 'else-else-c2'
} else {
return 'else-else-else'
}
}
}
以上还只是写了不多的几个条件,如果很多就会变的很麻烦,不好维护,因此,我们把上面的代码扁平化再看看
const a = 1
const b = 1
const c = 1
const elseValue = 99 // else 统一标识为 99 ,可以自定义
if(a===1 && b===1 && c===1){
return 'a1-b1-c1'
}else if(a===1 && b===1 && c===2){
return 'a1-b1-c2'
}else if(a===1 && b===1 && c===elseValue){
return 'a1-b1-else'
} else if(a===1 && b===2 && c===1){
return 'a1-b2-c1'
}else if(a===1 && b===2 && c===2){
return 'a1-b2-c2'
}else if(a===1 && b===2 && c===elseValue){
return 'a1-b2-else'
} else if(a===1 && b===elseValue && c===1){
return 'a1-else-c1'
}else if(a===1 && b===elseValue && c===2){
return 'a1-else-c2'
}else if(a===1 && b===elseValue && c===elseValue){
return 'a1-else-else'
}
else
if(a===2 && b===1 && c===1){
return 'a2-b1-c1'
}else if(a===2 && b===1 && c===2){
return 'a2-b1-c2'
}else if(a===2 && b===1 && c===elseValue){
return 'a2-b1-else'
} else if(a===2 && b===2 && c===1){
return 'a2-b2-c1'
}else if(a===2 && b===2 && c===2){
return 'a2-b2-c2'
}else if(a===2 && b===2 && c===elseValue){
return 'a2-b2-else'
} else if(a===2 && b===elseValue && c===1){
return 'a2-else-c1'
}else if(a===2 && b===elseValue && c===2){
return 'a2-else-c2'
}else if(a===2 && b===elseValue && c===elseValue){
return 'a2-else-else'
}
else
if(a===elseValue && b===1 && c===1){
return 'else-b1-c1'
}else if(a==elseValue && b===1 && c===2){
return 'else-b1-c2'
}else if(a===elseValue && b===1 && c===elseValue){
return 'else-b1-else'
} else if(a===elseValue && b===2 && c===1){
return 'else-b2-c1'
}else if(a===elseValue && b===2 && c===2){
return 'else-b2-c2'
}else if(a===elseValue && b===2 && c===elseValue){
return 'else-b2-else'
} else if(a===elseValue && b===elseValue && c===1){
return 'else-else-c1'
}else if(a===elseValue && b===elseValue && c===2){
return 'else-else-c2'
}else if(a===elseValue && b===elseValue && c===elseValue){
return 'else-else-else'
}
我们再进一步处理和优化
const a = 1
const b = 1
const c = 1
const ValidValue = [1,2]
const elseValue = 99
// a 和 b、c的枚举值只能是约定好的 1 或 2,即使后面要加,我们在map对象里面也是很好加的,管理和维护一个对象更方便,else 我们统一标识为 99,你可以定义成其它
// 我们创建一个对象,依此来维护一个对象
const map = {
'{"a":1,"b":1,"c":1}':()=>'a1-b1-c1',
'{"a":1,"b":1,"c":2}':()=>'a1-b1-c2',
'{"a":1,"b":1,"c":99}':()=>'a1-b1-else',
'{"a":1,"b":2,"c":1}':()=>'a1-b2-c1',
'{"a":1,"b":2,"c":2}':()=>'a1-b2-c2',
'{"a":1,"b":2,"c":99}':()=>'a1-b2-else',
'{"a":1,"b":99,"c":1}':()=>'a1-else-c1',
'{"a":1,"b":99,"c":2}':()=>'a1-else-c2',
'{"a":1,"b":99,"c":99}':()=>'a1-else-else',
'{"a":2,"b":1,"c":1}':()=>'a2-b1-c1',
'{"a":2,"b":1,"c":2}':()=>'a2-b1-c2',
'{"a":2,"b":1,"c":99}':()=>'a2-b1-else',
'{"a":2,"b":2,"c":1}':()=>'a2-b2-c1',
'{"a":2,"b":2,"c":2}':()=>'a2-b2-c2',
'{"a":2,"b":2,"c":99}':()=>'a2-b2-else',
'{"a":2,"b":99,"c":1}':()=>'a2-else-c1',
'{"a":2,"b":99,"c":2}':()=>'a2-else-c2',
'{"a":2,"b":99,"c":99}':()=>'a2-else-else',
'{"a":99,"b":1,"c":1}':()=>'else-b1-c1',
'{"a":99,"b":1,"c":2}':()=>'else-b1-c2',
'{"a":99,"b":1,"c":99}':()=>'else-b1-else',
'{"a":99,"b":2,"c":1}':()=>'else-b2-c1',
'{"a":99,"b":2,"c":2}':()=>'else-b2-c2',
'{"a":99,"b":2,"c":99}':()=>'else-b2-else',
'{"a":99,"b":99,"c":1}':()=>'else-else-c1',
'{"a":99,"b":99,"c":2}':()=>'else-else-c2',
'{"a":99,"b":99,"c":99}':()=>'else-else-else',
}
// map 的key 你也可以 这样写,如下
/*
const map = {
[JSON.stringify({a:1,b:1,c:1})]:()=>'a1-b1-c1'
}
*/
/**@description: 简化函数 对象key为对象字符串
* @param { Object } o: 对象 map
* @param { Object } c :条件对象
* @param { String | Function } d :没有匹配到 给的默认值 或是 执行的默认函数
*/
const simplifyFunc = function (o, c, d) {
const keys = Object.keys(o)
if (!keys.length) throw Error('不接受空对象')
const index = keys.findIndex(i => JSON.stringify(JSON.parse(i)) === JSON.stringify(c))
const val = o[keys[index]]
if (typeof val === 'string') return val
if (typeof val === 'function') return val()
if (!val && typeof d === 'string') return d
if (!val && typeof d === 'function') return d()
}
simplifyFunc(map,{
a:ValidValue.includes(a)?a:elseValue,
b:ValidValue.includes(b)?b:elseValue,
c:ValidValue.includes(c)?c:elseValue
})
以上两个代码简洁优化的核心原理 是创建一个对象,对象的key转换成了 字符串数组 或是 字符串对象,我们维护一个对象比维护一大堆if else 更方便
switch
switch 优化的第一反应应该是对象,但是也看具体情况
function previewWeek(i){
switch(i){
case 1:
return '星期一'
break;
case 2:
return '星期二'
break;
case 3:
return '星期三'
break;
case 4:
return '星期四'
break;
case 5:
return '星期五'
break;
case 6:
return '星期六'
break;
case 7:
return '星期日'
break;
default:
return ''
}
}
用 对象形式优化
function previewWeek(i){
return ({
1:'星期一',
2:'星期二',
3:'星期三',
4:'星期四',
5:'星期五',
6:'星期六',
7:'星期日',
})[i] || ''
}
是不一样了,代码确实少了很多,其实观察其特征,不用对象也是可以优化的,这特征就是规律,键值都是对应的,1的值是 '星期一',都是 1,和索引有关系,那么索引和数组有关系,那么我们用数组试试呢
function previewWeek(i){
return i>0 && i<8 ?'星期'+['一','二','三','四','五','六','日'][i-1]:''
}
于是我们懂得了,方法很多,只是未能找到最适合的那个,就写到这里吧,感谢你的阅读,如果对你有用,求个赞
结语
如果你有更好的想法,欢迎留言
文中若有不准确或错误的地方,欢迎指出