本章讨论了有关控制结构的一些一般性问题,实用性很强,在后面的小节还有部分结构化编程的理论知识。
19.1布尔表达式
除了最简单的、要求语句按顺序执行的控制结构之外,所有的控制结构都依赖于布尔表达式。
用true和false做布尔判断
在布尔表达式中应该用标识符true和false,而不要用0和1。
//优化前
if(reportSelected===1){}
if(summarySelected===0){}
//优化1
if(reportSelected===true){}
//优化2
if(reportSelected){}
上面的代码并没有反应出1和0是代表真还是假,if(reportSelected===1){}很可能是代表第一份报告,语意不明确。
也不建议布尔判断显示写成if(reportSelected===true){},这样同样会增加阅读代码的人需要记住的项数,隐式判断if(reportSelected){}的写法最佳。
简化复杂的表达式
- 拆分复杂判断并引入新的布尔变量 ```javascript //优化前 if(variate_A>variate_B||variate_C>variate_D||variate_E>variate_F){}
//优化后 let condition_A = variate_A>variate_B let condition_B = variate_C>variate_D let condition_C = variate_E>variate_F if(condition_A||condition_B||condition_C){}
- **把复杂的表达式做成布尔函数**
如果某项判断需要重复做或者会扰乱对程序主流程的理解,可以把判断的代码提取成函数,使用其返回值。<br />优化前:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1166162/1614593803113-ba991f09-23cc-4e29-bc9a-7dee24429eae.png#align=left&display=inline&height=94&margin=%5Bobject%20Object%5D&name=image.png&originHeight=94&originWidth=490&size=51336&status=done&style=none&width=490)<br />优化后:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1166162/1614593930527-e10004a0-ab15-4c77-a32b-5db298ea8e1f.png#align=left&display=inline&height=69&margin=%5Bobject%20Object%5D&name=image.png&originHeight=69&originWidth=470&size=35555&status=done&style=none&width=470)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1166162/1614593946242-eba8d729-8955-489f-8080-6da69a60d29e.png#align=left&display=inline&height=209&margin=%5Bobject%20Object%5D&name=image.png&originHeight=209&originWidth=482&size=118958&status=done&style=none&width=482)<br />这样顺着代码主流程阅读时,可以先不去看那个复杂的判断从而打断思路。如果这个判断只用一次,最好也放到一个子程序中,改善可读性。
- **用决策表替代复杂的条件**
详见第18章表驱动法。
<a name="kuHVo"></a>
#### 编写肯定形式的布尔表达式
大多数人在理解一长串否定用语的时候会觉得困难,可以采用措施避免引入复杂否定的形式。
- **互换if和else中的代码**
```javascript
//修改前
if(!statusOK){
//do something else
}else{
//do something else
}
//修改后
if(statusOK){
//do something else
}else{
//do something else
}
变换变量的名字
//修改前
if(!statusOK){
//do something else
}else{
//do something else
}
//修改后
if(ErrorDetected){
//do something else
}else{
//do something else
}
用狄默根定理简化否定判断
//修改前
if(!displayOk||!printerOk){}
//修改后
if(!(displayOk&&printerOk)){}
狄默根定理揭示了一个表达式和另一个含义相同但却以双重否定形式表达的表达式之间的逻辑关系。
用括号使布尔表达式更清晰
一个复杂的表达式,预期依赖于语言的求值顺序,不如用括号更清楚地表达意图。
//修改前
if(a<b==c==d){}
//修改后
if((a<b)==(c==d)){}
理解布尔表达式是如何求值的
在求布尔表达式的值的时候,JS的编译器是采用短路或者惰性求值,只求出必须的部分。
let a = 0
let b = 1
if(a===0||(b/c)){
console.log(1)
}
上面代码中就存在短路求值行为,在确定a===0的时候求值过程就结束了,即使c是未定义的变量。但是当第一个条件为假时,程序就报错了,而且即使程序为真时能够正常运行,也会让阅读代码的人感到不解。所以不建议依赖求值顺序和短路求值。
按照数轴的顺序编写数值表达式
这个原则的关键点在于要从左到右、从小到大排列元素。 ```javascript MIN_ELEMENTS <=i and i <=MAX_ELEMENTS i<MIN_ELEMENTS or MAX_ELEMENTS<i
//作者认为上述表达式比下面的更能给读者这是在判断什么的提示 i>=MIN_ELEMENTS and i <=MAX_ELEMENTS
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1166162/1614599330437-16a54f50-9203-4273-88fa-c237f2c36118.png#align=left&display=inline&height=158&margin=%5Bobject%20Object%5D&name=image.png&originHeight=158&originWidth=332&size=21775&status=done&style=none&width=332)
<a name="v00Ub"></a>
#### 与0比较的指导原则
```javascript
//0代表逻辑变量的时候,隐式写法比较合适
while(!done){}
//0代表数字的时候,需要采用显示写法
while(done!==0){}
19.2复合语句(块)
例如JS中的for循环,可以通过把括号对一起写出,还有用括号把条件表达清楚两种方式,可以有效地使用符合语句。
19.3空语句
在C++中,可以使分号自占一行、空括号或者定义DoNothing()函数在强调空语句,还可以考虑换用一个非空循环体让代码更清晰。
感觉JS中不太出现这样的情况,本章知识点可以忽略。
19.4驯服危险的深层嵌套
研究表明,很少人能够快速理解超过三层的if嵌套,而深层嵌套业余第五章中所讲的管理复杂度的使命是违背的。
下面是一些避免深层嵌套的方法。
通过重复检测条件中的某一部分来简化嵌套的if语句
不可能无偿减少嵌套层次,所以可能必须容忍使用一个更复杂的判断。
//修改前
if(inputStatus == inputStatus_Success){
//lots of code
if(printerRoutine!=null){
//lots of code
if(SetupPage()){
//lots of code
if(AllocMem()){
//lots of code
}
}
}
}
//修改后
if(inputStatus == inputStatus_Success){
//lots of code
if(printerRoutine!=null){
//lots of code
}
}
if((inputStatus == inputStatus_Success)&&(printerRoutine!=null)&&SetupPage()){
//lots of code
if(AllocMem()){
//lots of code
}
}
用return语句来简化嵌套
作者说这种技巧不常见,只有团队熟悉了这种技巧才纳入团队编码规范。
//修改后
if(inputStatus != inputStatus_Success){
return
}
//lots of code
if(printerRoutine===null){
return
}
//lots of code
if(!SetupPage()){
return
}
//lots of code
if(!AllocMem()){
return
}
//lots of code
把嵌套if转换成一组if-else if- else语句
//修改前
if(10<quantity){
if(100<quantity){
if(1000<quantity){
discount = 0.1
}else{
discount = 0.05
}
}else{
discount = 0.025
}
}else{
discount = 0.0
}
//修改后
if(1000<quantity){
discount = 0.1
}else if(100<quantity){
discount = 0.05
}else if(10<quantity){
discount = 0.025
}else{
discount = 0.0
}
嵌套if转换成case语句
switch(true){ //不能写switch(quantity),因为表达式值为bool类型
case quantity > 0 && quantity <= 10:
discount = 0.0
break
case quantity > 10 && quantity <= 100:
discount = 0.05
break
case quantity > 59 && quantity <= 80:
discount = 0.1
break
default:
break;
}
把深层嵌套的代码抽取出来放进单独的子程序
可以把if内部判断的内容拆分成单独的子程序,这里的ifelse也可以用switch case替代。
在面向对象的环境中,可以创建一个抽象的Transaction基类,派生出Deposit、Withdrawal和Transfer子类来。
还可以用Factory Method模式来替换其中的swtich语句。
19.5编程基础:结构化编程
结构化编程的核心思想是,一个应用程序应该只采用一些单入单出的控制结构。单入单出是指一个代码块,只能从一个位置开始执行,并且只能结束于一个位置。
一个结构化的程序会按照一种有序且有规则的方式执行,不会做不可预知的跳转。规则性不强的方法,很难反映程序的执行过程,导致程序的低质量。
结构化编程的中心论点是,任何一种控制流都可以由顺序、选择、迭代三种结构生成,对三种标准的结构化编程结构之外的任何控制结构都要持批判态度。
19.6控制结构与复杂度
控制结构影响程序的复杂度,而程序的复杂度影响代码的可读性,也影响着程序运行的错误率。
下图给出了度量复杂度的方法和对应的结果。