前言
很久之前,发了一篇文章:[浅析]特定场景下取代if-else和switch的方案,但是关于使用 if-else 的场景可不会仅仅是上面文章那么少,还有很多的场景,今天再次写下在开发上有哪些可以代替或者优化 if-else
的场景。
这里强调代替或者优化
if-else
,是在特定场景下进行的。目的就是为了在特定场景下改善代码,让代码简洁。增加代码的可读性,维护性,复用性。如果if-else
使用的场景比较简单,或者代替,优化if-else
后会对代码产生不好的影响。就不建议使用别的方案代替或者优化,不能为了不写if-else
而不写,不能为了优化而优化。
1.范围查询
比如抽取中奖的号码区间,中奖的号码区间分别是 9-12,14-18,然后需要判断号码是否中奖了,逻辑很简单就实现了
代码语言:javascript复制let num1= 15
let num2= 13
if((num1>=9 && num1<=12)||(num1>=14 && num1<=18)){
// 中奖了
}
if((num2>=9 && num2<=12)||(num2>=14 && num2<=18)){
// 中奖了
}
这样写貌似没什么问题,如果以后需求变了,中奖的号码不是9-12或者14-18。而是 10-14 或者 18-20。这样就要改原来的代码逻辑
代码语言:javascript复制if((num1>=10 && num1<=14)||(num1>=18 && num1<=20)){
// 中奖了
}
或者又有其他需求,比如中奖号码是 9-12,14-18,20-22或者26-30。还是要改动一下 if-else 的逻辑
代码语言:javascript复制if((num1>=9 && num1<=12)||(num1>=14 && num<=18)||(num1>=20 && num1<=22)||(num2>=26 && num1<=30)){
// 数字在范围内
}
现在可以用some 进行封装一个函数,只需要一次封装,往后的需求如果范围区间改变了,就可以
代码语言:javascript复制function handleCheckRange(num, ...ranges){
return ranges.some(item=>num>=item[0]&&num<=item[1])
}
handleCheckRange(num1,[9,12],[14,18]) //true
handleCheckRange(num2,[9,12],[14,18]) //false
handleCheckRange(num2,[10,14],[18,20]) //true
handleCheckRange(num2,[9,12],[14,18],[20,22],[26,30]) //false
1.可能有人会想到如果再有需求,判断条件不是 >= 或者 <= 。可能是 < 和 >,<= 和 >,或者 < 和 >=呢?这样
handleCheckRange
也是需要改了。这种情况确实,遇到其他的判断条件是需要改一下这个方法。但是改一下这个方法并不难,直接封装就好 2.如果判断条件太多变化,一定要封装方法兼容其他的判断条件,可以参考我的笔记:练习题5:模拟实现开闭区间,这里就不展开讲了。
2.多个与条件
相信大家都会遇到可能会有多个条件组合的问题
比如有一个参与热卖活动赠积分活动,活动状态(status
),预热中(status=1
),进行中(status=2
)。用户类型(type
)也有分普通用户(type=1
)vip用户(type=2
)
规则是: 1.在预热中参与活动,vip用户赠送 1000 积分,普通用户赠送 700 积分。 1.在进行中参与活动,vip用户赠送 800 积分,普通用户赠送 300 积分。
之前写法是
代码语言:javascript复制let status=1
let type=2
if(status===1){
if(type===1){
console.log('普通用户在预售中参与活动,赠送700积分')
}
else if(type===2){
console.log('vip用户在预售中参与活动,赠送1000积分')
}
}
else if(status===2){
if(type===1){
console.log('普通用户在进行中参与活动,赠送300积分')
}
else if(type===2){
console.log('vip用户在进行中参与活动,赠送800积分')
}
}
// 或者
if(status===1&&type===1){
console.log('普通用户在预售中参与活动,赠送700积分')
}
else if(status===1&&type===2){
console.log('vip用户在预售中参与活动,赠送1000积分')
}
else if(status===2&&type===1){
console.log('普通用户在进行中参与活动,赠送300积分')
}
else if(status===2&&type===2){
console.log('vip用户在进行中参与活动,赠送800积分')
}
但是现在可以使用 obj写法,这样下来,如果以后有什么条件改了,直接改 obj 这个配置就好。
代码语言:javascript复制let obj={
'status=1&type=1':'普通用户在预售中参与活动,赠送700积分',
'status=1&type=2':'vip用户在预售中参与活动,赠送1000积分',
'status=2&type=1':'普通用户在进行中参与活动,赠送300积分',
'status=2&type=2':'普通用户在进行中参与活动,赠送800积分'
}
console.log(obj[`status=${status}&type=${type}`])
3.多个或条件
比如输一个城市名字,输出一个省份名称
代码语言:javascript复制let city='广州'
if(city==='广州'||city==='佛山'){
console.log('广东')
}
else if(city==='海口'||city==='三亚'){
console.log('海南')
}
这样写的弊端就是,如果 if-else
数量一多,代码就会多,而且判断的条件会变得很长,还有一个问题就是风格有可能不会统一
下面用其他方法进行优化下 方法一
代码语言:javascript复制let arr=[
{
city:'广州',
province:'广东'
},
{
city:'佛山',
province:'广东'
},
{
city:'海口',
province:'海南'
},
{
city:'三亚',
province:'海南'
}
]
console.log(arr.filter(item=>item.city===city)[0].province)//广东
方法二
代码语言:javascript复制let city='广州'
let obj={
'广州':'广东',
'佛山':'广东',
'海口':'海南',
'三亚':'海南',
}
console.log(obj[city])// 广东
见到 广东 和 海南 写了这么多次,可能大家都会郁闷,一个省城市有 20 个,就要写 20 次,可能就有人会想到把城市都作为 key
,然后判断传进来的 city
是否在 key
里面,代码如下。
let city='广州'
let obj={
'广州,佛山':'广东',
'海口,三亚':'海南',
}
for(let key in obj){
if(key.includes(city)){
console.log(obj[key])
break
}
}
//广东
但是弊端也出来了,就是上面的方法有 bug
,只输出城市名称的其中一个字,也能输出对应的省份。
let city='州'
let obj={
'广州,佛山':'广东',
'海口,三亚':'海南',
}
for(let key in obj){
if(key.includes(city)){
console.log(obj[key])
break
}
}
//广东
要解决这个问题,两个方式,一种是把 key
转成数组再判断,或者使用 Map
// 方式一:把 key 转成数组再判断
let city='广州'
let obj={
'广州,佛山':'广东',
'海口,三亚':'海南',
}
let keys=[]
for(let key in obj){
keys=key.split(',')
if(keys.includes(city)){
console.log(obj[key])
break
}
}
// 方式二:使用Map
let city='广州'
let map=new Map([
[['广州','佛山'],'广东'],
[['海口','三亚'],'海南'],
])
for (let key of map.keys()) {
if(key.includes(city)){
console.log(obj[key])
break
}
}
//广东
4.无规律 if-elseif
这样情况就是 if-elseif
没什么规则可寻。比如有需求是学生评选奖学金候选人活动
1.学生综合素质得分(quality>=90
)90 以上,平均绩点 4.5 以上,成为一等奖学金候选人。
2.学生综合素质得分 80 以上(quality>=80
),每科绩点均不低于 2.5 ,成为二等奖学金候选人。
3.学生综合素质得分不低于 75(quality>=75
),获得过其他奖项荣誉,成为三等奖学金候选人。
let studentInfo={
name:'守候',
chinese:4.6,
math:4.7,
english:4,
computer:4.8,
quality:93,
prizes:['搬砖','扫地']
}
let subjectAll=['chinese','math','english','computer']
function handle (student,subject) {
function getAverage(){
let sum=0
subject.forEach(item=>sum =student[item])
return sum/subject.length
}
function checkScore(){
return subject.every(item => student[item]>2.5)
}
if(student.quality>=90&&getAverage()>4.5){
console.log('成为一等奖学金候选人')
// 其他代码
}
else if(student.quality>=80&&checkScore()){
console.log('成为二等奖学金候选人')
// 其他代码
}
else if(student.quality>=75&&student.prizes.length>0){
console.log('成为三等奖学金候选人')
// 其他代码
}
}
handle(studentInfo,subjectAll) //成为一等奖学金候选人
代码看着很简单就完成了,但是实际开发中,if-elseif
的层级数量,每一个 if-elseif
判断逻辑和里面的执行操作的代码会远超过这个例子。那么 handle
这个方法的代码会很长,方法会变得巨大。对代码的可读性,维护性,复用性都有很多影响。如果需求有变动,还需要把整个函数的逻辑都理清楚。
上面的代码主要还是要处理好 if-elseif
。但是这些 if-else
并没有规矩可寻,所以只能全部都单独抽取出来,所有的都封装成函数。
function handle (student,subject) {
function getAverage(){
let sum=0
subject.forEach(item=>sum =student[item])
return sum/subject.length
}
function checkScore(){
return subject.every(item => student[item]>2.5)
}
let rules=[
{
rule(){
return student.quality>=90&&getAverage()>4.5
},
fn(){
console.log('成为一等奖学金候选人')
// 其他代码
}
},
{
rule(){
return student.quality>=80&&checkScore()
},
fn(){
console.log('成为二等奖学金候选人')
// 其他代码
}
},
{
rule(){
return student.quality>=75&&student.prizes.length>0
},
fn(){
console.log('成为三等奖学金候选人')
// 其他代码
}
}
]
for(let item of rules){
if(item.rule()){
item.fn()
// 执行完函数马上返回,不再执行下一次循环
return
}
}
}
这是只要有 item.fn()
有执行到,说明该同学已经成为了候选人,把返回回来就好。
这样一下,代码也完全符合 之前的 if-elseif
的逻辑。可能会有人有疑问,这样一改代码没减少反而变多了,但是实际上,这个代码已经是分为一块一块的,可读性和维护性已经有所改善,如果需要有那里改动,我们也不需要理会整个函数,只需要改动 rules
。
5.多重 if
还有一种情况是,几个 if-else
连着执行的代码,
比如有一个页面,能显示用户记录的足迹,也可以让用户选择记录自己的足迹,记录的范围可以是省,是市,是区。那么代码就需要实现一个省市区,多选联动。逻辑就是选了省,未必会选择市,但是选了市就必然会选了省,以此类推。下面简单写一下伪代码。
代码语言:javascript复制function initPostion(){
//所有省份列表
let provinceList=['广东','广西','海南','....']
//已选省份下辖的所有城市
let cityList=[]
//已选城市下辖的所有区
let districtList=[]
//已选择省份
let selectedProvinces=['广东','广西']
//已选择城市
let selectedCitys=['广州']
//已选择区
let selectedDistricts=['天河区']
//如果选了省份
if (selectedProvinces.length>0) {
//根据 selectedProvinces 获取 cityList 的逻辑
//其他代码
}
if (selectedCitys.length>0) {
//根据 selectedCitys 获取 districtList 的逻辑
//其他代码
}
//其他初始化显示的逻辑
}
伪代码一写,想必有开发者已经看出问题了。所有的 if
都偶尔在一起了, 而且 if 里面的代码逻辑可能会很多长。如果以后需求改了,要求选了国家,再选省市区,或者选择区之后,还能选择镇和村。到时候 if
会变多,initPostion
整体代码会变得巨大。维护起来会比较吃力,同时也容易出错。
解决这问题,可以把 if
拆分为函数
function initPostion(){
//所有省份列表
let provinceList=['广东','广西','海南','....']
//已选省份下辖的所有城市
let cityList=[]
//已选城市下辖的所有区
let districtList=[]
//已选择省份
let selectedProvinces=['广东','广西']
//已选择城市
let selectedCitys=['广州']
//已选择区
let selectedDistricts=['天河区']
let handleObj={
provinces(){
//如果选了省份
if (selectedProvinces.length>0) {
//根据 selectedProvinces 获取 cityList 的逻辑
//其他代码
}
},
city(){
if (selectedCitys.length>0) {
//根据 selectedCitys 获取 districtList 的逻辑
//其他代码
}
}
}
let handleFns=['provinces','city']
for(let fnName of handleFns){
handleObj[fnName]()
}
//其他初始化显示的逻辑
}
可能这样看着代码是多了,但是管理起来会比原来的方案容易管理,每一块 if
都被拆分为一个函数,如果需要改动某一块代码,就改某一块就行了,不需要对其他的代码进行改动。如果有需求上的变动,就是改 handleObj
的属性函数,以及 handleFns
的顺序就行了。
小结
好了,关于 if-else
的代替和优化方案的第二篇文章到此为止了。这里再次强调,代替和优化 if-else
语句,建议是在特定场景下使用特定的方案进行。切记不能为了代替而代替,不能为了优化而优化。