JavaScript複雜判斷的優雅寫法
前提
我們寫js程式碼時常遇到複雜邏輯判斷的情況,通常大家可以用if/else或switch來實現多個條件判斷,但這樣會有個問題,隨著邏輯複雜度的增加,程式碼中的if/else/switch會變得越來越臃腫,越來越看不懂,那麼如何更優雅的寫判斷邏輯,本文帶你去試一下。
舉例
先看一段程式碼
/** * 按钮点击事件 * @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') }else if(status == 5){ sendLog('cancel') jumpTo('CancelPage') }else { sendLog('other') jumpTo('Index') } }复制代码
透過程式碼可以看到這個按鈕的點擊邏輯:依照不同活動狀態做兩件事情,發送日誌埋點和跳到對應頁面,大家可以很輕易的提出這段程式碼的改寫方案,switch出場:
/** * 按钮点击事件 * @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的邏輯。
這時有同學會說,還有更簡單的寫法:
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) }复制代码
上面程式碼確實看起來更清爽了,這種方法的聰明之處在於:將判斷條件作為對象的屬性名,將處理邏輯作為物件的屬性值,在按鈕點擊的時候,透過物件屬性尋找的方式來進行邏輯判斷,這種寫法特別適合一元條件判斷的情況。
是不是還有其他寫法呢?有的:
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"鍵。
- 一個物件的鍵只能是字串或Symbols,但一個Map的鍵可以是任意值。
- 你可以透過size屬性很容易地得到一個Map的鍵值對個數,而物件的鍵值對個數只能手動確認。
我們需要把問題升級一下,以前按鈕點擊時候只需要判斷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){ //do sth }else if(status == 5){ //do sth }else { //do sth } }else if(identity == 'master') { if(status == 1){ //do sth }else if(status == 2){ //do sth }else if(status == 3){ //do sth }else if(status == 4){ //do sth }else if(status == 5){ //do sth }else { //do sth } } }复制代码
原諒我不寫每個判斷裡的具體邏輯了,因為程式碼太冗長了。
原諒我又用了if/else,因為我看到很多人依然在用if/else寫這種大段落的邏輯判斷。
從上面的例子我們可以看到,當你的邏輯升級為二元判斷時,你的判斷量會加倍,你的程式碼量也會加倍,這時怎麼寫更清爽呢?
const actions = new Map([ ['guest_1', ()=>{/*do sth*/}], ['guest_2', ()=>{/*do sth*/}], ['guest_3', ()=>{/*do sth*/}], ['guest_4', ()=>{/*do sth*/}], ['guest_5', ()=>{/*do sth*/}], ['master_1', ()=>{/*do sth*/}], ['master_2', ()=>{/*do sth*/}], ['master_3', ()=>{/*do sth*/}], ['master_4', ()=>{/*do sth*/}], ['master_5', ()=>{/*do sth*/}], ['default', ()=>{/*do sth*/}], ])/** * 按钮点击事件 * @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
- 一元判斷時:存到Object裡
- 一元判斷時:存到Map裡
- 多元判斷時:將condition拼接成字串存到Object裡
- 多元判斷時:將condition拼接成字串存到Map裡
- 多元判斷時:將condition存為Object存到Map裡
- 多元判斷時:將condition寫作正規存到Map裡
至此,本文也將告一段落,願你未來的人生裡,不只是有if/else/switch。
如果你對本文有興趣,請關注作者微信公眾號:「大轉轉fe」
推薦教學:《JavaScript基礎教學》
以上是JavaScript複雜判斷的優雅寫法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

公司安全軟件導致部分應用無法正常運行的排查與解決方法許多公司為了保障內部網絡安全,會部署安全軟件。 ...

H5、小程序和APP的主要區別在於:技術架構:H5基於網頁技術,小程序和APP為獨立應用程序。體驗和功能:H5輕便易用,功能受限;小程序輕量級,交互性好;APP功能強大,體驗流暢。兼容性:H5跨平台兼容,小程序和APP受平台限制。開發成本:H5開發成本低,小程序中等,APP最高。適用場景:H5適合信息展示,小程序適合輕量化應用,APP適合複雜功能應用。

React的主要功能包括組件化思想、狀態管理和虛擬DOM。 1)組件化思想允許將UI拆分成可複用的部分,提高代碼可讀性和可維護性。 2)狀態管理通過state和props管理動態數據,變化觸發UI更新。 3)虛擬DOM優化性能,通過內存中的DOM副本計算最小操作更新UI。

本文提供國內安全下載歐易OKX App的詳細指南。由於國內應用商店限制,建議用戶通過歐易OKX官方網站下載App,或使用官網提供的二維碼掃描下載。下載過程中,務必核實官網地址,檢查應用權限,安裝後進行安全掃描,並啟用雙重驗證。 使用過程中,請遵守當地法律法規,使用安全網絡環境,保護賬戶安全,警惕詐騙,理性投資。 本文僅供參考,不構成投資建議,數字資產交易風險自負。

公司安全軟件與應用兼容性問題及排查方法許多企業為了保障內網安全,會安裝安全軟件。然而,安全軟件有時...

H5開發工具推薦:VSCode、WebStorm、Atom、Brackets、Sublime Text;小程序開發工具:微信開發者工具、支付寶小程序開發者工具、百度智能小程序IDE、頭條小程序開發者工具、Taro。
