这一篇,来聊聊混乱的if、else
是怎么来的,怎么和他们说再见,以及写的一个工具函数atcon
。
一、代码是怎样混乱的
最简单的一个需求:根据活动改变二维码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| qrcode = 'img_a';
// 活动二 if (activity === 2) { qrcode = 'img_b'; // 活动三 } else if (activity === 3) { qrcode = 'img_c'; // 活动四 } else if (activity === 4) { qrcode = 'img_d'; // 活动五 } else if (activity === 5) { qrcode = 'img_e'; // 活动六 } else if (activity === 6) { qrcode = 'img_f'; }
|
嗯,这个逻辑看起来还是挺简单的。这时候,我们增加一个变量notice
,因为需要根据活动改变文案,而且只在某些特殊情况变更,于是它可能变成了这样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| qrcode = 'img_a'; notice = '文案一';
// 活动二 if (activity === 2) { qrcode = 'img_b'; // 活动三 } else if (activity === 3) { qrcode = 'img_c'; notice = '文案三'; // 活动四 } else if (activity === 4) { qrcode = 'img_d'; // 活动五 } else if (activity === 5) { qrcode = 'img_e'; notice = '文案五'; // 活动六 } else if (activity === 6) { qrcode = 'img_f'; }
|
看起来也没什么。但是这时候,产品跑来说,活动下线了,二维码需要做区分。于是代码可能又变成了这样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| qrcode = 'img_a'; notice = '文案零';
// 活动二 if (activity === 2) { qrcode = 'img_b'; if (isEnd) { qrcode = 'img_h'; }
// 活动三 } else if (activity === 3) { qrcode = 'img_c'; notice = '文案三'; if (isEnd) { qrcode = 'img_i'; } // 活动四 } else if (activity === 4) { qrcode = 'img_d'; if (isEnd) { qrcode = 'img_j'; } // 活动五 } else if (activity === 5) { qrcode = 'img_e'; notice = '文案五'; if (isEnd) { qrcode = 'img_k'; } // 活动六 } else if (activity === 6) { qrcode = 'img_f'; if (isEnd) { qrcode = 'img_l'; } }
if (isEnd) { notice = '下线了'; }
|
是不是开始有点懵逼了?Hold on,隔了几天,产品又跑来跟我们说,活动三、四、五效果不是特别好,我们准备改一版样式,做下ABTEST看看效果。
这意味着我们的代码里又要增加一个变量,于是我们只能说,卧槽。
二、如何改造
或者说,如何在一开始给自己留好后路。
我们先从第1步重新开始,如果用对象保存这些关联,是不是就会简单很多呢。
1 2 3 4 5 6 7 8
| imgMap = { '2': 'img_b', '3': 'img_c', '4': 'img_d', '5': 'img_e', '6': 'img_f' }; qrcode = imgMap[activity] || 'img_a';
|
第2步,增加的notice,其实和qrcode没有任何关联。
1 2 3 4 5
| noticeMap = { '3': '文案三', '5': '文案五' }; notice = noticeMap[activity] || '文案零';
|
活动下线区分,我们稍稍改变下结构好了,几分钟的事。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| imgMap = { online: { '2': 'img_b', // .....etc. }, offline: { '2': 'img_h', // .....etc. } }; var noticeMap = { online: { '3': '文案三', '5': '文案五' }, offline: '下线了' }; qrcode = imgMap[isEnd ? 'offline' : 'online'][activity] || 'img_a';
if (isEnd) { notice = noticeMap.offline; } else { notice = noticeMap.online[activity] || '文案零'; }
|
上面,我们的编码方式,整体还是挺不错的,每种状态都差不多能对应一个结果。
但是,有没有发现,我们在notice
的处理上,又开始写if、else
了。
这里先不提,我们继续下面一步。
如果只是对在线的活动二、三做abtest,并改变里面的二维码和图片,我们该怎么做呢?
如果采用上面imgMap一一对应的关系,数据结构就变成了这样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| imgMap = { online: { '2': { a: 'img_b', b: 'img_o' }, '3': { a: 'img_b', b: 'img_p' }, '4': { a: 'img_c', d: 'img_c' }, // .....etc. }, offline: { '2': { a: 'img_h', b: 'img_h' }, '3': { a: 'img_b', b: 'img_b' }, '4': { a: 'img_c', d: 'img_c' }, // .....etc. } };
|
有没有发现,为了保证我们可以一次性访问到具体对应的值,我们做了非常多重复配置。
我们当然不希望进行重复配置,而是希望处理的结构可以像这样简单:
1 2 3 4 5 6 7 8 9 10 11 12 13
| var noticeMap = { online: { '2': { a: 'abtest文案二' }, '3': { a: 'abtest文案三', b: '文案三' }, '5': '文案五' }, offline: '下线了' };
|
但是我们怎么提取对应的值,又如何避免提取的过程不会出错呢。
像上面notice一样,我们只能写if、else
判断代码,外加循环判断对应属性的值是否存在,好像又要重走以前的老路。
三、使用 atcon
atcon就是用来解决这个问题,封装了属性值提取、reduce循环判断等处理,让大家可以拥有清晰美妙的结构的同时,也能非常方便地提取对应条件分支的值。
使用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| imgMap = { online: { '2': { a: 'img_b', b: 'img_o' }, '3': { a: 'img_b', b: 'img_p' }, '4': 'img_c', '5': 'img_d', '6': 'img_e' }, offline: { '2': 'img_h', '3': 'img_i', '4': 'img_j', '5': 'img_k', '6': 'img_l' }, __DEFAULT__: 'img_a' };
noticeMap = { online: { '2': { a: 'abtest文案二' }, '3': { a: 'abtest文案三', b: '文案三' }, '5': '文案五' }, offline: '下线了' __DEFAULT__: '文案零' };
isImg = img => img.search(/\.(png|jpg)$/) !== -1; isString = str => Object.prototype.toString.call(str) === '[object String]';
qrcode = atcon(imgMap, [isEnd ? 'offline' : 'online', activity, isAbtest ? 'a' : 'b'], isImg); notice = atcon(noticeMap, [isEnd ? 'offline' : 'online', activity, isAbtest ? 'a' : 'b'], isString);
|
atcon将我们一开始的默认值,也通过__DEFAULT__
的提取,做了指定。因此,复杂的分支判断,只要先通过细致设想的结构,然后与atcon完美地结合起来,就可以开始和if else
说再见了。
四、结构变更
如果这时候,需要把所有abtest的a,文案全部改成一样的,而下线的文案保持跟上线的文案一致,我们甚至能够直接改变noticeMap结构。
1 2 3 4 5 6 7 8 9
| noticeMap = { b: { '3': '文案三', '5': '文案五' }, a: '一样的ABTEST文案', __DEFAULT__: '文案零' }; notice = atcon(noticeMap, [isAbtest ? 'a' : 'b', activity], isString);
|
我们改变思路、结构的同时,改变atcon第二个数组参数的数组项位置和数量,就达到了调整复杂if else
逻辑的目的。
五、最后
我们不改逻辑,基本不改代码,我们只改配置。
从现在起,告别混乱的if else
。
具体见:atcon
思路来源