經(jīng)常code review,我發(fā)現(xiàn)JS newbie很容易寫出一堆冗長(zhǎng)的代碼。今天就列幾個(gè)比較常見的“解決之道”,看看如何減少JS里的條件判斷。
提前返回,少用if...else
“if...else是編程語(yǔ)言的精華?!斞浮?br>
但是過(guò)多的嵌套,還是挺令人抓狂的。這里有一個(gè)很典型的條件嵌套:
function func(){
var result;
if( conditionA ) {
if( condintionB ) {
result = 'Success';
} else {
result = 'Error1';
}
} else {
result = 'Error2'
}
return result;
}
這種嵌套的特點(diǎn)就是else里的代碼塊很小,但是由于不得不做的分支語(yǔ)句導(dǎo)致多層嵌套。動(dòng)動(dòng)腦筋,怎樣精簡(jiǎn)一下呢?在if里做非判斷——條件反轉(zhuǎn),并通過(guò)衛(wèi)語(yǔ)句提前return else分支。
function func(){
if( !conditionA ) {
return 'Error2'
}
if( !condintionB ) {
return 'Error1'
}
return 'Success';
}
forEach優(yōu)化
遍歷的時(shí)候也經(jīng)常產(chǎn)生大量的嵌套,如下代碼所示,我們先對(duì)數(shù)組元素做一次合法性校驗(yàn),通過(guò)后再做一次新的操作,最后把操作結(jié)果追加到新數(shù)組里。
const func = (arr) => {
const res = []
arr.forEach( (e) => {
if( e !== 'Onion' ){
res.push(`${e} Run!`)
}
})
return res;
}
仔細(xì)觀察這就是一個(gè)filter加map的過(guò)程。我們不妨試試函數(shù)式編程:
const func = (arr) => {
return arr
.filer( (e) => e !== 'Onion' )
.map( (e) => `${e} Run!` );
}
多條件,用Array.includes
再舉個(gè)例子,某個(gè)頁(yè)面需要檢驗(yàn)輸入type是否合法。我收到過(guò)一個(gè)MR曾經(jīng)是這么寫的。
const init(type) {
if( type === 'Seminar' || type === 'Interview' ) {
console.log('valid');
}
...
console.error('invalide');
}
如果合法的類型只有兩種,代碼其實(shí)也沒啥問題。只是一般的業(yè)務(wù)很容易有后續(xù)的延展。今后將合法類型增加到10種的話,上述代碼里將是一大長(zhǎng)串的if判斷。這種代碼可讀性極差,我們不如轉(zhuǎn)換一下思想,把非法類型儲(chǔ)到數(shù)組里,用Array.includes來(lái)幫你完成冗長(zhǎng)的判斷。之后每增加一種新的類型,只需在數(shù)組后追加新字符串就行了。
const init(type) {
const invalidArray = ['Seminar', 'Interview']
if( invalidArray.includes(type) ) {
console.log('valid');
}
...
}
使用object索引
類似的情況也出現(xiàn)在三元表達(dá)式里:
const dateFormat = (dateTime) => {
const format = this.$i18n.locale === 'en' ? 'mmm d, yyyy' : 'yyyy年m月d日';
return DateFormat(dateTime, format);
}
我們現(xiàn)階段多語(yǔ)言只有en和zh,上述代碼自然不是問題,但是也難保哪天會(huì)支持日語(yǔ)——ja。這時(shí)候再寫成下面這類代碼就很搞笑了:
const format = this.$i18n.locale === 'en' ?
'mmm d, yyyy' :
(this.$i18n.locale === 'zh' ?
'yyyy年m月d日' : 'yyyy/m/d');
比較合適的寫法就是使用object鍵索引,這樣當(dāng)語(yǔ)言業(yè)務(wù)擴(kuò)展后,只需要在localeFormats后追加新格式就行了。
const localeFormats = {
en: 'mmm d, yyyy',
zh: 'yyyy年m月d日',
ja: 'yyyy/m/d',
}
const format = localeFormats[this.$i18n.locale];
盡量少用swith
長(zhǎng)Switch也及其難看。
export function(type) {
switch( type ) {
case 'Onion':
return func1();
case 'Garlic':
return func2();
case 'Ginger':
return func3();
default:
return () => {console.error('ERROR')};
}
}
我記得OOP設(shè)計(jì)模式里提到過(guò):盡量使用多態(tài)和繼承代替Switch結(jié)構(gòu)。JS里倒不必非得往這方面想,用Object或是Map索引來(lái)代替Switch也是極好滴!
const arr = [
['Onion', func1],
['Garlic', func2],
['Ginger', func3],
]
const def = () => {console.error('ERROR')}
const vegetable = new Map(arr);
export function(type) {
return ( vegetable.get(type) || def ).call(null);
}
Optional Chaining
Optional Chaining(以下簡(jiǎn)稱OC)是我極力推薦的一個(gè)語(yǔ)法糖。我寫過(guò)一期《Javascript Optional Chaining》具體介紹它的用法,有興趣的小伙伴可以看一看,這里稍微點(diǎn)一下。比如我們想獲取地址里的街道信息,但是并不確定地址本身是否存在,因此只能在獲取街道前,事先判斷一下地址合法性,一般我們會(huì)這么寫:
if( address ) {
var street = address.street;
}
但假如再多一層呢,從basicInfo.address.street這樣找下來(lái)呢?
if( basicInfo ) {
var address = basicInfo.address;
if( address ) {
var street = address.street;
}
}
上面的代碼我已經(jīng)覺得很丑陋了,再多個(gè)幾層真是沒法看了。不過(guò)不用擔(dān)心,有了OC一切迎刃而解。(雖然OC還在ECMAScript stage2,但是大家可以用babel嘗鮮;babel會(huì)自動(dòng)把如下源碼轉(zhuǎn)義成上面的形式)
var street = basicInfo?.address?.street;
寫在最后
自學(xué)過(guò)幾種編程語(yǔ)言,我感覺這類小技巧在各類語(yǔ)言中大同小異;實(shí)現(xiàn)上也許有細(xì)微差別,但基本思想都是一樣的——減少大括號(hào)的數(shù)量?。?br> 我在面試的時(shí)候經(jīng)??吹揭恍┤藢懘a及其冗長(zhǎng),嵌套極多。我提醒后,得到的回復(fù)一般是:“我沒做過(guò)這種面試”,“我熟悉的是另外一種語(yǔ)言”等等?;剡^(guò)頭來(lái)再想想,這真的是好理由嗎?