壹 ❀ 引
在JavaScript開發中,條件判斷語句的使用頻率是極高的,而對於條件判斷簡單易讀的if else應該都是大家的首選。可是代碼寫的久了,我們總是希望自己的代碼看着能更為簡潔規范(逼格更高),那么今天我們就由淺到深介紹幾種實用小技巧,幫大家減少代碼中的if else。說在開頭,本文並未有消滅或歧視 if else的意思,if else的好用都知道,這里只是在某些特定場景為大家額外提供一種思路罷了,如何使用還請自行抉擇,那么本文開始。
貳 ❀ 短路求值
在函數定義時,常有若函數調用未提供參數則使用默認值的情景,當然我們可以使用if else來解決這個問題:
function fn(name) { if(!name){ name = '聽風是風'; }; console.log(name); }; fn();//聽風是風 fn('行星飛行');//行星飛行
有沒有更優雅的做法呢?當然,我們可以使用短路求值,像這樣:
function fn(name) { name = name || '聽風是風'; console.log(name); }; fn();//聽風是風 fn('行星飛行');//行星飛行
我們簡單復習下 ||或 和 &&與 的概念,||表示兩者任意一個為真便為真,&&表示兩者都為真才是真,任意一個為假就是假。
為什么這個特定能用在變量賦值呢?其實這是利用了 || 前者為真后者不判斷,&&前者為假后者不判斷的特點,來看個例子:
function fn() { console.log(1); }; true || fn(); //不執行 false && fn(); //不執行 false || fn(); //1 true && fn() //1
所以上面的短路求值中,當name有值時后面的默認值就被忽略了不判斷,而name無值時便會判斷后者取到默認值。
短路求值除了用在變量賦值外,還能用於函數調用,比如在下方例子為假時才調用某個方法:
let name = false; function fn() { console.log(1); }; //if if (!name) { fn();//1 }; //短路 !name && fn();//1
對於函數形參短路賦值其實有個缺點,假設我的參數就是0,false或者null,因為短路的特性會被認為假,這樣我們無法拿到想要的值,更佳的做法是使用ES6的形參默認值,像這樣:
function fn(param) { param = param || 1; console.log(param); }; fn(0); //1 fn(null); //1 fn(false); //1 //使用形參默認值 function fn1(param = 1) { console.log(param); }; fn1(); //1 fn1(0); //0 fn1(null); //null fn1(false); //false
叄 ❀ 三元運算符
三元運算符我想大家都不會陌生,在開發中三元運算的使用場景其實非常多,比如我希望為條件為 true時變量為1,反之為0,通過三元運算符我們可以這樣做:
let blo = true; let num; if (blo) { num = 1; } else { num = 0; }; console.log(num);//1 //三元運算符 blo =false; blo ? num = 1 : num = 0; console.log(num);//0
比如我們希望條件為true時調用函數fn,為false時什么也不做,使用三元看起來也會更加舒服:
let blo = true; let fn = function () { console.log(1); }; //if if (blo) { fn(); //1 }; //三元 blo ? fn() : null;//1
在開發中函數常常需要 return 一份數據回去,有時候根據條件不同我們可能要分別對應返回不同的數據,三元也能解決這個問題:
let fn = function () { let flo = true; if (flo) { return 1; } else { return 2; }; }; let f = fn(); //1 let fn1 = function () { let flo = true; //三元 return flo ? 1 : 2; }; let f1 = fn1();//1
三元結合return的操作非常適合我們遞歸處理時做收尾工作,如果滿足條件繼續遞歸,不滿足跳出遞歸,比如我們要求正整數N到0之間所有整數之和,可以這么寫:
let result = 0; function add(n){ result += n return n>=2 ? add(n-1) : result; }; let num = add(10);//55
怎么樣?看着是不是特別簡潔舒服。需要注意的是,三元運算符的表達式只能是單語句,否則無法使用,比如下方例子中由於執行語句超過了2句,這就無法使用三元運算符改寫了:
let i = 5; if (i > 0) { //執行語句超過2句 console.log(1); i = 0; };
肆 ❀ switch case
短路求值與三元運算符固然好用,但其實有一個遺憾,它們都只能解決非A即B的條件判斷,凡是條件判斷超過兩種就顯得十分無力了。那難道我們只能使用 else if 嗎,其實可以使用switch case。
例如A情況我們希望A情況輸出a,B情況輸出b,C情況輸出c,其它情況輸出d,用 else if 與switch case分別是這樣:
let name = 'B'; //if else if if (name === 'A') { console.log('a'); } else if (name === 'B') { console.log('b'); } else if (name === 'C') { console.log('c'); } else { console.log('d'); }; //switch case switch (name) { case 'A': console.log('a'); break; case 'B': console.log('b'); break; case 'C': console.log('c'); default: console.log('d'); };
那么我們希望A或B情況輸出1,C情況輸出2,其它情況輸出3呢,switch case其實也能做到:
let name = 'B'; //if else if if (name === 'A' || name === 'B') { console.log(1); } else if (name === 'C') { console.log(2); } else { console.log(3); }; //switch case switch (name) { case 'A': case 'B': console.log(1); break; case 'C': console.log(2); default: console.log(3); };
當然我想大多數人還是會覺得switch case寫起來賊麻煩,盡管它的可讀性確實比 else if 更高,沒關系,就算作為了解也沒有壞處。
伍 ❀ 對象配置
條件超過三種,else if 寫起來不太優雅,switch case又覺得麻煩,有沒有更棒的做法呢?我在實際開發遇到過這樣一個情景,我需要根據用戶不同的操作類型對同一份數據進行不同加工,比如新增,修改,刪除等。那么我用else if是這么做的:
function del() { //刪除操作 }; function add() { //新增 }; function update() { //更新 }; function process(operateType) { if (operateType === 'del') { del() } else if (operateType === 'add') { add() } else if (operateType === 'update') { update() }; }; process('del');//刪除
一種很棒的做法就是通過對象配置,將你的操作類型作為key,具體操作的函數作為value,像這樣:
function del() { //刪除操作 }; function add() { //新增 }; function update() { //更新 }; let typeFn = { 'del': del, 'add': add, 'update': update }; function process(operateType) { typeFn[operateType](); }; process('del'); //刪除
怎么樣,有沒有眼前一亮呢?我們將需求升級,現在除了判斷操作type類型外,還得額外附加一個狀態類型,else if是這樣,這里簡單描述下:
function process(operateType, status) { if (operateType === 'del' && status === 1) { del() } else if (operateType === 'add'&& status === 2) { add() } else if (operateType === 'update'&& status === 3) { update() }; };
不太優雅,通過對象配置做法,我們其實只用將參數簡單配置就OK了,像這樣是不是更清爽呢:
let typeFn = { 'del_1': del, 'add_2': add, 'update_3': update }; function process(operateType,status) { typeFn[`${operateType}_${status}`](); }; process('del',1); //刪除
什么,對象配置的調用方式語義化不太明顯?那各位可曾對ES6的map數據結構有了解呢,如果你覺得這樣的調用不太實在,我們再改改,將調用條件與函數配置成map數據,像這樣:
let typeFn = new Map([ ['del_1', function () {/*do something*/ }], ['add_2', function () {/*do something*/ }], ['update_3', function () {/*do something*/ }], ]); function process(operateType, status) { typeFn.get(`${operateType}_${status}`)(); }; process('del', 1); //刪除
我們通過map數據的get方法去數據中找到方法執行,這下可讀性總強一點了吧,諸君可否滿意呢?
陸 ❀ 數組配置
在處理條件判斷時,我們常會遇到條件與對應結果全部已知的情況,比如我們要根據用戶的經驗設置等級頭銜,[0,100)--萌新,[100,200)--騎士,[200,300)--英雄,[300-無限大]--傳說,那么用else if怎么寫已經沒有懸念了:
function youAreMyHero(experience) { if (experience < 100) { return '萌新'; } else if (experience < 200 && experience >= 100) { return '騎士'; } else if (experience < 300 && experience >= 200) { return '英雄'; } else if (experience >= 300) { return '傳說'; }; }; let level = youAreMyHero(351); //傳說
對於這種條件與結果已知的情況,我們其實可以通過數組配置的形式將條件結果抽離出來,像這樣:
function youAreMyHero(param) { let experience = [300, 200, 100]; let level = ['傳說', '英雄', '騎士', '萌新']; for (let i = 0; i < experience.length; i++) { if (param >= experience[i]) { return level[i]; }; }; return level[level.length - 1]; }; let level = youAreMyHero(250); //英雄
這么做的好處就是便於管理條件與執行結果,如果后面新增了等級判斷,我們不用去修改業務邏輯中的 else if 語句長度,只用單純維護我們抽離的數據即可。
柒 ❀ 總
那么到這里,我們大致介紹了五種可取代if else的方式,我們知道短路運算符除了短路求值,還能用於函數調用;三元運算符也不僅僅是處理變量賦值,在return場景結合三元用起來居然如此舒適。
在文章后半段,我們還了解了對象配置,利用map數據結構,以及數據實行來解決特殊場景。我並不推薦為了追求高逼格而犧牲代碼可讀性,但我更希望在你以后的代碼中不僅僅只有if else,那么到這里本文結束。
參考