咱们编写js程式码时常常遇到复杂逻辑判断的状况,一般你们能够用if/else或者switch来实现多个条件判断,但这样会有个问题,随着逻辑复杂度的增长,程式码中的if/else/switch会变得愈来愈臃肿,愈来愈看不懂,那么如何更优雅的写判断逻辑,本文带你试一下。javascript
先看一段程式码html
/** * 按钮点选事件 * @param {number} status活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消 */ const onButtonClick = ( status )=> { if (status == 1 ){ sendLog( 'processing' ) jumpTo( 'IndexPage' ) } else if (status == 2 ){ sendLog( 'fail' ) jumpTo( 'FailPage' ) } else if (status == 3 ) { sendLog( 'fail' ) jumpTo('FailPage') } else if(status== 4){ sendLog('success') jumpTo('SuccessPage') } 不然 if(status == 5){ sendLog('cancel') jumpTo('CancelPage') } else { sendLog('other') jumpTo('Index') } }
经过程式码能够看到这个按钮的点选逻辑:根据不一样活动状态作两件事情,传送日志埋点和跳转到对应页面,你们能够很轻易的提出这段程式码的改写方案,switch出场:java
/** * 按钮点选事件 * @param {number} status活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消 */ const onButtonClick = ( status )=> { switch (status){ case 1 : sendLog( 'processing' ) jumpTo( 'IndexPage' ) break; case 2 : case 3 : sendLog( 'fail' ) jumpTo( 'FailPage' ) break; case 4 : sendLog( 'success' ) jumpTo('SuccessPage') break; case 5: sendLog('cancel') jumpTo('CancelPage') break; default: sendLog('other') jumpTo('Index') break; } }
嗯,这样看起来比if/else清晰多了,细心的同窗也发现了小技巧,case 2和case 3逻辑同样的时候,能够省去执行语句和break,则case 2的状况自动执行case 3的逻辑。es6
这时有同窗会说,还有更简单的写法:ide
const actions = { '1':[ 'processing','IndexPage' ], '2':[ 'fail','FailPage' ], '3':[ 'fail','FailPage' ], '4':[ 'success','SuccessPage' ], '5':[ 'cancel','CancelPage' ], 'default':[ 'other','Index' ], } /** * 按钮点选事件 * @param {number} status活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消 */ const onButtonClick = (status)=> { let action = actions [status] || actions [ 'default' ], logName = action [ 0 ], pageName = action [ 1 ]; sendLog(logName) jumpTo(pageName) }
上面程式码确实看起来更清爽了,这种方法的聪明之处在于:将判断条件做为对象的属性名,将处理逻辑做为对象的属性值,在按钮点选的时候,经过对象属性查询的方式来进行逻辑判断,这种写法特别适合一元条件判断的状况。优化
是否是还有其余写法呢?有的:this
const actions = new Map([ [ 1,[ 'processing','IndexPage' ]], [ 2,[ 'fail','FailPage' ]], [ 3,[ 'fail','FailPage' ]], [ 4,[ 'success','SuccessPage' ]], [ 5,[ 'cancel','CancelPage' ]], [ 'default',[ 'other','Index']] ]) / * * * 按钮点选事件 * @param {number} status活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消 */ const onButtonClick = ( status )=> { let action = actions .get(status) || actions.get( 'default' ) ; sendLog(action[ 0 ]) jumpTo(action[ 1 ]) }
这样写用到了es6里的Map物件,是否是更爽了?Map物件和Object有什么区别呢?prototype
咱们须要把问题升级一下,之前按钮点选时候只须要判断status,如今还须要判断使用者的身份:日志
/* * * 按钮点选事件 * @param {number} status活动状态:1开团进行中2开团失败3开团成功4商品售罄5有库存未开团 * @param {string} identity身份标识: guest客态master主态 */ const onButtonClick = (status,identity)=>{ if (identity == 'guest' ){ if (status == 1 ){ //do sth } else if (status == 2 ) { //do sth } else if (status == 3 ){ //do sth } else if (status == 4){ //作某事 } else if(status== 5){ //作某事 } else { //作某事 } } else if(identity == 'master'){ if(status == 1){ //作某事 } else if(status== 2){ //作某事 } 其余 if(status== 3){ //作某事 } else if(status== 4 } { //作某事 } else if(状态== 5){ //作某事 } else { //作某事 } } }
原谅我不写每一个判断里的具体逻辑了,由于程式码太冗长了。code
原谅我又用了if/else,由于我看到不少人依然在用if/else写这种大段的逻辑判断。
从上面的例子咱们能够看到,当你的逻辑升级为二元判断时,你的判断量会加倍,你的程式码量也会加倍,这时怎么写更清爽呢?
const actions = new Map([ [ 'guest_1',()=> { / * do sth * / }], [ 'guest_2',()=> { / * do sth * / }], [ 'guest_3',()=> { / *作某事* / }], [ 'guest_4',()=> { / *作某事* / }], [ 'guest_5',()=> { / *作某事* / } ], [ 'master_1',()=> { / *作某事* / }], [ 'master_2',()=> { / *作某事* / }], [ 'master_3',()=> { / *作某事* /}], [ 'master_4', () => { /*do sth*/ }], [ 'master_5', () => { /*do sth*/ }], [ 'default', () => { /*do sth*/ }], ]) /* * javascript * 按钮点选事件 * @param {string} identity身份标识:guest客态master主态 * @param {number} status活动状态:1开团进行中2开团失败3开团成功4商品售罄5有库存未开团 */ const onButtonClick = (identity,status)=>{ let action = actions.get(`${identity}_${status}` ) || actions.get( 'default' ) action.call(this) }
上述程式码核心逻辑是:把两个条件拼接成字串,并经过以条件拼接字串做为键,以处理函式做为值的Map进行查询并执行,这种写法在多元条件判断时候尤为好用。
固然上述程式码若是用Object来实现也是相似的:
const actions = { 'guest_1':()=>{/*do sth*/}, 'guest_2':()=>{/*do sth*/}, //.... } const onButtonClick = (identity,status)=>{ let action = actions[`${identity}_${status}`] || actions['default'] action.call(this) }
若是有些同窗以为把查询条件拼成字串有点别扭,那还有一种方案,就是用Map,以Object做为key:
const actions = new Map([ [{identity:'guest',status:1},()=>{/*do sth*/}], [{identity:'guest',status:2},()=>{/*do sth*/}], //... ]) const onButtonClick = (identity,status)=>{ let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status)) action.forEach(([key,value])=>value.call(this)) }
是否是又高阶了一点点?
这里也看出来Map与Object的区别,Map能够用任何型别的资料做为key。
咱们如今再将难度升级一点点,假如guest状况下,status1-4的处理逻辑都同样怎么办,最差的状况是这样:
const actions = new Map([ [{identity:'guest',status:1 },()=> { / * functionA * / }], [{identity:'guest',status:2 },()=> { / * functionA * / }], [{identity:'guest',status:3 },()=> { / * functionA * / }], [{identity:'guest',status:4 },()=> { / * functionA * / }], [{identity:'guest',status:5 },()=> { / * functionB * / }], // ... ])
好一点的写法是将处理逻辑函式进行快取:
const actions = ()=>{ const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} return new Map([ [{identity:'guest',status:1},functionA], [{identity:'guest',status:2},functionA], [{identity:'guest',status:3},functionA], [{identity:'guest',status:4},functionA], [{identity:'guest',status:5},functionB], //... ]) } const onButtonClick = (identity,status)=>{ let action = [...actions()].filter(([key,value])=>(key.identity == identity && key.status == status)) action.forEach(([key,value])=>value.call(this)) }
这样写已经能知足平常需求了,但认真一点讲,上面重写了4次functionA仍是有点不爽,假如判断条件变得特别复杂,好比identity有3种状态,status有10种状态,那你须要定义30条处理逻辑,而每每这些逻辑里面不少都是相同的,这彷佛也是笔者不想接受的,那能够这样实现:
const actions = ()=>{ const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} return new Map([ [/^guest_[1-4]$/,functionA], [/^guest_5$/,functionB], //... ]) } const onButtonClick = (identity,status)=>{ let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`))) action.forEach(([key,value])=>value.call(this)) }
这里Map的优点更加凸显,能够用正则做为key了,这样就有了无限可能,假如需求变成,凡是guest状况都要传送一个日志埋点,不一样status状况也须要单独的逻辑处理,那咱们能够这样写:
const actions = ()=>{ const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} const functionC = ()=>{/*send log*/} return new Map([ [/^guest_[1-4]$/,functionA], [/^guest_5$/,functionB], [/^guest_.*$/,functionC], //... ]) } const onButtonClick = (identity,status)=>{ let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`))) action.forEach(([key,value])=>value.call(this)) }
也就是说利用阵列回圈的特性,符合正则条件的逻辑都会被执行,那就能够同时执行公共逻辑和单独逻辑,由于正则的存在,你能够开启想象力解锁更多的玩法,本文就不赘述了。
本文已经教你了8种逻辑判断写法,包括:
至此,本文也将告一段落,愿你将来的人生里,不仅是有if/else/switch。