详解写好JS条件语句的5条守则
在用JavaScript工作时,我们经常和条件语句打交道,这里有5条让你写出更好/干净的条件语句的建议。
目录:
1.多重判断时使用Array.includes
2.更少的嵌套,尽早return
3.使用默认参数和解构
4.倾向于遍历对象而不是Switch语句
5.对所有/部分判断使用Array.every&Array.some
6.总结
1.多重判断时使用Array.includes
让我们看一下下面这个例子:
//condition functiontest(fruit){ if(fruit=='apple'||fruit=='strawberry'){ console.log('red'); } }
第一眼,上面这个例子看起来没问题。如果我们有更多名字叫cherry和cranberries的红色水果呢?我们准备用更多的||来拓展条件语句吗?
我们可以用Array.includes(Array.includes)重写条件语句。
functiontest(fruit){ constredFruits=['apple','strawberry','cherry','cranberries']; if(redFruits.includes(fruit)){ console.log('red'); } }
我们把红色的水果(redfruits)这一判断条件提取到一个数组。这样一来,代码看起来更整洁。
2.更少的嵌套,尽早Return
让我们拓展上一个例子让它包含两个条件。
- 如果没有传入参数fruit,抛出错误
- 接受quantity参数,并且在quantity大于10时打印出来
functiontest(fruit,quantity){ constredFruits=['apple','strawberry','cherry','cranberries']; //条件1:fruit必须有值 if(fruit){ //条件2:必须是red的 if(redFruits.includes(fruit)){ console.log('red'); //条件3:quantity大于10 if(quantity>10){ console.log('bigquantity'); } } }else{ thrownewError('Nofruit!'); } } //测试结果 test(null);//error:Nofruits test('apple');//print:red test('apple',20);//print:red,bigquantity
在上面的代码,我们有:
- 1个if/else语句筛选出无效的语句
- 3层if嵌套语句(条件1,2&3)
我个人遵循的规则一般是在发现无效条件时,尽早Return。
/_当发现无效语句时,尽早Return_/ functiontest(fruit,quantity){ constredFruits=['apple','strawberry','cherry','cranberries']; //条件1:尽早抛出错误 if(!fruit)thrownewError('Nofruit!'); //条件2:必须是红色的 if(redFruits.includes(fruit)){ console.log('red'); //条件3:必须是大质量的 if(quantity>10){ console.log('bigquantity'); } } }
这样一来,我们少了一层嵌套语句。这种编码风格非常好,尤其是当你有很长的if语句的时候(想象你需要滚动到最底层才知道还有else语句,这并不酷)
我们可以通过倒置判断条件&尽早return进一步减少if嵌套。看下面我们是怎么处理判断条件2的:
/_当发现无效语句时,尽早Return_/ functiontest(fruit,quantity){ constredFruits=['apple','strawberry','cherry','cranberries']; //条件1:尽早抛出错误 if(!fruit)thrownewError('Nofruit!'); //条件2:当水果不是红色时停止继续执行 if(!redFruits.includes(fruit))return; console.log('red'); //条件3:必须是大质量的 if(quantity>10){ console.log('bigquantity'); } }
通过倒置判断条件2,我们的代码避免了嵌套语句。这个技巧在我们需要进行很长的逻辑判断时是非常有用的,特别是我们希望能够在条件不满足时能够停止下来进行处理。
而且这么做并不困难。问问自己,这个版本(没有嵌套)是不是比之前的(两层条件嵌套)更好,可读性更高?
但对于我,我会保留先前的版本(包含两层嵌套)。这是因为:
- 代码比较短且直接,包含if嵌套的更清晰
- 倒置判断条件可能加重思考的负担(增加认知载荷)
因此,应当尽力减少嵌套和尽早return,但不要过度。如果你感兴趣的话,可以看一下关于这个话题的一篇文章和StackOverflow上的讨论。
- AvoidElse,ReturnEarlybyTimOxley
- StackOverflowdiscussiononif/elsecodingstyle
3.使用默认参数和解构
我猜下面的代码你可能会熟悉,在JavaScript中我们总是需要检查null/undefined的值和指定默认值:
functiontest(fruit,quantity){ if(!fruit)return; //如果quantity参数没有传入,设置默认值为1 constq=quantity||1; console.log(`Wehave${q}${fruit}!`); } //testresults test('banana');//Wehave1banana! test('apple',2);//Wehave2apple!
实际上,我们可以通过声明默认函数参数来消除变量q。
functiontest(fruit,quantity=1){ //如果quantity参数没有传入,设置默认值为1 if(!fruit)return; console.log(`Wehave${quantity}${fruit}!`); } //testresults test('banana');//Wehave1banana! test('apple',2);//Wehave2apple!
这更加直观,不是吗?注意,每个声明都有自己的默认参数.
例如,我们也能给fruit分配默认值:functiontest(fruit='unknown',quantity=1)。
如果fruit是一个object会怎么样?我们能分配一个默认参数吗?
functiontest(fruit){ //当值存在时打印fruit的值 if(fruit&&fruit.name){ console.log(fruit.name); }else{ console.log('unknown'); } } //testresults test(undefined);//unknown test({});//unknown test({name:'apple',color:'red'});//apple
看上面这个例子,我们想打印fruit对象中可能存在的name属性。否则我们将打印unknown。我们可以通过默认参数以及解构从而避免判断条件fruit&&fruit.name
//解构-仅仅获取name属性 //为其赋默认值为空对象 functiontest({name}={}){ console.log(name||'unknown'); } //testresults test(undefined);//unknown test({});//unknown test({name:'apple',color:'red'});//apple
由于我们只需要name属性,我们可以用{name}解构出参数,然后我们就能使用变量name代替fruit.name。
我们也需要声明空对象{}作为默认值。如果我们不这么做,当执行test(undefined)时,你将得到一个无法对undefined或null解构的的错误。因为在undefined中没有name属性。
如果你不介意使用第三方库,这有一些方式减少null的检查:
- 使用Lodashget函数
- 使用Facebook开源的idx库(withBabeljs)
这是一个使用Lodash的例子:
functiontest(fruit){ //获取属性名,如果属性名不可用,赋默认值为unknown console.log(__.get(fruit,'name','unknown'); } //testresults test(undefined);//unknown test({});//unknown test({name:'apple',color:'red'});//apple
你可以在jsbin运行demo代码。除此之外,如果你是函数式编程的粉丝,你可能选择使用Lodashfp,Lodash的函数式版本(方法变更为get或者getOr)。
4.倾向于对象遍历而不是Switch语句
让我们看下面这个例子,我们想根据color打印出水果:
functiontest(color){ //使用条件语句来寻找对应颜色的水果 switch(color){ case'red': return['apple','strawberry']; case'yellow': return['banana','pineapple']; case'purple': return['grape','plum']; default: return[]; } } //testresults test(null);//[] test('yellow');//['banana','pineapple']
上面的代码看起来没有错误,但是我找到了一些累赘。用对象遍历实现相同的结果,语法看起来更简洁:
constfruitColor={ red:['apple','strawberry'], yellow:['banana','pineapple'], purple:['grape','plum'] }; functiontest(color){ returnfruitColor[color]||[]; }
或者你也可以使用Map实现相同的结果:
constfruitColor=newMap() .set('red',['apple','strawberry']) .set('yellow',['banana','pineapple']) .set('purple',['grape','plum']); functiontest(color){ returnfruitColor.get(color)||[]; }
Map是一种在ES2015规范之后实现的对象类型,允许你存储key和value的值。
但我们是否应当禁止switch语句的使用呢?答案是不要限制你自己。从个人来说,我会尽可能的使用对象遍历,但我并不严格遵守它,而是使用对当前的场景更有意义的方式。
ToddMotto有一篇关于switch语句对比对象遍历的更深入的文章,你可以在这个地方阅读
TL;DR;重构语法
在上面的例子,我们能够用Array.filter重构我们的代码,实现相同的效果。
constfruits=[ {name:'apple',color:'red'}, {name:'strawberry',color:'red'}, {name:'banana',color:'yellow'}, {name:'pineapple',color:'yellow'}, {name:'grape',color:'purple'}, {name:'plum',color:'purple'} ]; functiontest(color){ returnfruits.filter(f=>f.color==color); }
有着不止一种方法能够实现相同的结果,我们以上展示了4种。
5.对所有/部分判断使用Array.every&Array.some
这最后一个建议更多是关于利用JavaScriptArray的内置方法来减少代码行数。看下面的代码,我们想要检查是否所有水果都是红色:
constfruits=[ {name:'apple',color:'red'}, {name:'banana',color:'yellow'}, {name:'grape',color:'purple'} ]; functiontest(){ letisAllRed=true; //条件:所有水果都是红色 for(letfoffruits){ if(!isAllRed)break; isAllRed=(f.color=='red'); } console.log(isAllRed);//false }
代码那么长!我们可以通过Array.every减少代码行数:
constfruits=[ {name:'apple',color:'red'}, {name:'banana',color:'yellow'}, {name:'grape',color:'purple'} ]; functiontest(){ constisAllRed=fruits.every(f=>f.color=='red'); console.log(isAllRed);//false }
现在更简洁了,不是吗?相同的方式,如果我们想测试是否存在红色的水果,我们可以使用Array.some一行代码实现。
constfruits=[ {name:'apple',color:'red'}, {name:'banana',color:'yellow'}, {name:'grape',color:'purple'} ]; functiontest(){ //条件:任何一个水果是红色 constisAnyRed=fruits.some(f=>f.color=='red'); console.log(isAnyRed);//true }
6.总结
让我们一起生产更多可读性高的代码。我希望你能从这篇文章学到东西。也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。