总结下自己最近的代码简洁悟道,值得一阅

3,411 阅读9分钟

最近带项目组简单做了一下 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]:''
}

于是我们懂得了,方法很多,只是未能找到最适合的那个,就写到这里吧,感谢你的阅读,如果对你有用,求个赞

结语

如果你有更好的想法,欢迎留言

文中若有不准确或错误的地方,欢迎指出