[淺析] 特定場景下代替優化 if-else 的方案 (二)

有一再有二,有二再有三。把未知的變成已知,就成了本身的知識javascript

前言

好久以前,發了一篇文章:[淺析]特定場景下取代if-else和switch的方案,可是關於使用 if-else 的場景可不會僅僅是上面文章那麼少,還有不少的場景,今天再次寫下在開發上有哪些能夠代替或者優化 if-else 的場景。java

這裏強調代替或者優化 if-else,是在特定場景下進行的。目的就是爲了在特定場景下改善代碼,讓代碼簡潔。增長代碼的可讀性,維護性,複用性。若是 if-else 使用的場景比較簡單,或者代替,優化 if-else 後會對代碼產生很差的影響。就不建議使用別的方案代替或者優化,不能爲了避免寫 if-else 而不寫,不能爲了優化而優化。git

1.範圍查詢

好比抽取中獎的號碼區間,中獎的號碼區間分別是 9-12,14-18,而後須要判斷號碼是否中獎了,邏輯很簡單就實現了github

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。這樣就要改原來的代碼邏輯數組

if((num1>=10 && num1<=14)||(num1>=18 && num1<=20)){
     // 中獎了
}
複製代碼

或者又有其餘需求,好比中獎號碼是 9-12,14-18,20-22或者26-30。仍是要改動一下 if-else 的邏輯微信

if((num1>=9 && num1<=12)||(num1>=14 && num<=18)||(num1>=20 && num1<=22)||(num2>=26 && num1<=30)){
     // 數字在範圍內
}
複製代碼

如今能夠用some 進行封裝一個函數,只須要一次封裝,日後的需求若是範圍區間改變了,就能夠函數

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 也是須要改了。這種狀況確實,遇到其餘的判斷條件是須要改一下這個方法。可是改一下這個方法並不難,直接封裝就好post

2.若是判斷條件太多變化,必定要封裝方法兼容其餘的判斷條件,能夠參考個人筆記:練習題5:模擬實現開閉區間,這裏就不展開講了。優化

2.多個與條件

相信你們都會遇到可能會有多個條件組合的問題ui

好比有一個參與熱賣活動贈積分活動,活動狀態(status),預熱中(status=1),進行中(status=2)。用戶類型(type)也有分普通用戶(type=1)vip用戶(type=2

規則是: 1.在預熱中參與活動,vip用戶贈送 1000 積分,普通用戶贈送 700 積分。 1.在進行中參與活動,vip用戶贈送 800 積分,普通用戶贈送 300 積分。

以前寫法是

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 這個配置就好。

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.多個或條件

好比輸入一個城市名字,輸出一個省份名稱

let city='廣州'
if(city==='廣州'||city==='佛山'){
    console.log('廣東')
}
else if(city==='海口'||city==='三亞'){
    console.log('海南')
}
複製代碼

這樣寫的弊端就是,若是 if-else 數量一多,代碼就會多,並且判斷的條件會變得很長,還有一個問題就是風格有可能不會統一

下面用其餘方法進行優化下 方法一

let arr=[
    {
        city:'廣州',
        province:'廣東'
    },
    {
        city:'佛山',
        province:'廣東'
    },
    {
        city:'海口',
        province:'海南'
    },
    {
        city:'三亞',
        province:'海南'
    }
]
console.log(arr.filter(item=>item.city===city)[0].province)//廣東
複製代碼

方法二

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 連着執行的代碼,

好比有一個頁面,能顯示用戶記錄的足跡,也可讓用戶選擇記錄本身的足跡,記錄的範圍能夠是省,是市,是區。那麼代碼就須要實現一個省市區,多選聯動。邏輯就是選了省,未必會選擇市,可是選了市就必然會選了省,以此類推。下面簡單寫一下僞代碼。

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和switch的方案

JavaScript 複雜判斷的更優雅寫法

小結

好了,關於 if-else 的代替和優化方案的第二篇文章到此爲止了。這裏再次強調,代替和優化 if-else 語句,建議是在特定場景下使用特定的方案進行。切記不能爲了代替而代替,不能爲了優化而優化。

若是以後還發現有另外的收穫會第一時間再寫文章。若是你們都該文章有什麼建議,見解,或者文章有什麼錯誤,改進的地方歡迎你們評論區留言

-------------------------華麗的分割線--------------------

想了解更多,和我交流,請添加我微信。或者關注個人微信公衆號:守候書閣

相關文章
相關標籤/搜索