编写逻辑更加复杂的代码时,会根据不同的条件来处理对应的代码,常用的编程要素包括本文介绍的比较运算、条件语句(if)、?:运算符、选择语句(switch),以及下一篇文章介绍的循环语句等语句结构。
JavaScript中的比较运算包括:
比较运算会判断条件是否成立,成立时返回true,不成立时返回false。和日常的比较运算相比,JavaScript代码中需要注意相等的比较,特别是字符串和数值的比较;其中,等于运算(==)会判断数据的字符串形式是否相同,而全等运算(===)则判断数据的类型和值是否都相同。如下面的代码。
<script> let x = "123"; let y = 123; alert(x == y); // true alert(x === y); // false </script>
本例,变量x的值为字符串"123",变量y的值为数值123,使用等于运算符(==)比较时,结果是成立的,而使用全等运算符时,结果是不成立的。
获取的数据为字符串时,如果明确需要数值之间的比较,可以先将字符串转换为数值格式,如使用parseInt()函数转换为整数,使用parseFloat()函数转换为浮点数,然后再对数值进行比较。
在JavaScript中还需要注意特殊值的比较结果,如undefined和undefined相比、null和null相比是全等的,而NaN与NaN却不相等;代码中可以使用Object.is(x,y)方法比较x和y是否全等,比===运算符更加符合逻辑,如NaN值的比较,使用==或===运算符比较两个NaN值时,结果为false;而Object.is(NaN,NaN)则会返回true。
JavaScript中还需要注意浮点数的比较,先来看下面的代码。
<script> let x = 0.1; let y = 10 % 3.3; alert(x === y); // false </script>
本例,变量x的值为0.1,变量y的结果口算为0.1,但实际上在前面的文章中已经讨论过,其计算结果为0.10000000000000053,这是由于计算机内部处理浮点数的精度问题造成的误差,所以,代码中变量x和y比较是否相等时会返回false。
为了避免浮点数精度问题对相等运算带来的影响,可以使用两种方法,一是通过截取固定的小数位进行比较;二是在比较时设置一个合理的误差范围。
截取固定的小数位可以使用数值对象的toFixed()方法,如下面的代码。
<script> let x = 0.1; let y = 10 % 3.3; alert(x.toFixed(4) === y.toFixed(4)); // true </script>
代码中,变量x和y各保留4位小数,然后进行比较,结果为true。
下面的代码,创建了比较两个浮点数是否相等的函数。
<script> // 判断浮点数x和y相差是否在指定的范围内 function fequals(x, y, range = 0.000001) { return Math.abs(x - y) <= range; } // let x = 0.1; let y = 10 % 3.3; alert(fequals(x,y)); // true </script>
代码中首先定义了fequals()函数,其中,参数x和y为需要比较的两个浮点数,参数range为允许的误差范围,默认为0.000001;函数中,使用Math.abs()方法计算参数x与y的差的绝对值,当绝对值小于或等于允许的误差范围时返回true,否则返回false。最后,使用fequals()函数比较变量x和y,它们的差的绝对值小于0.000001,返回结果为true。
if语句结构可以根据不同的条件执行对应的代码,最简单的应用格式如下。
if (<条件>) { <语句> }
结构中,当<条件>成立时执行<语句>,否则跳出if语句结构。如下面的代码演示了闰年的判断。
<script> let y = 2020; if (y % 400 === 0 || (y % 100 !== 0 && y % 4 === 0)) { alert(`${y}年是闰年`) } </script>
本例,闰年的条件有两种情况,第一种情况是当年份可以被400整除,即y % 400结果为0时;另一种情况是年份不能被100整除,但能够被4整除,即“y % 100 !== 0 && y % 4 === 0”,这里使用&&运算符表示两个条件必须同时成立。两种是闰年的情况使用||运算符,表示只要有一个成立即是闰年。
请注意,对于条件的组合,如果无法确定运算符的运算顺序,总是使用圆括号明确条件的组合是比较安全的方法,也更加方便阅读代码。
执行代码会显示“2020年是闰年”,可以修改变量y的值来观察执行情况,可以看到,如果年份不是闰年,则什么也不显示。这显然不是好的交互方式,此时,可以添加else语句结构,应用格式如下。
if (<条件>) { <语句1> } else { <语句2> }
此结构中,当<条件>成立时,执行<语句1>,否则执行<语句2>;下面的代码演示了如何使用if...else语句判断整数是偶数还是奇数。
<script> let n = 9; if (n % 2 === 0) { alert(`${n}是偶数`) } else { alert(`${n}是奇数`) } </script>
执行代码会显示“9是奇数”,可以修改变量n的值来观察运行结果。
如果有多个条件分支,还可以在if和else结构之间添加多个else if结构分别进行条件判断,应用结构如下。
if (<条件1>) { <语句1> } else if (<条件2>) { <语句2> } else if (<条件n>) { <语句n> } else { <语句n+1> }
此结构中,如果<条件1>成立则执行<语句1>,否则如果<条件2>成立则执行<语句2>,否则如果<条件n>成立则执行<语句n>;条件都不成立时执行<语句n+1>。下面的代码演示了相关应用。
<script> // 判断年份是否为闰 function isLeap(y) { return y % 400 === 0 || (y % 100 !== 0 && y % 4 === 0); } // let y = 2020; let m = 2; let d = -1; if ([1, 3, 5, 7, 8, 10, 12].indexOf(m) >= 0) { d = 31; } else if (m === 2) { d = (isLeap(y) ? 29 : 28); } else if ([4, 6, 9, 11].indexOf(m) >= 0) { d = 30; } // 显示结果 if (d > 0) alert(`${y}年${m}月有${d}天`); else alert("无效的日期信息"); </script>
代码中,首先定义了isLeap()函数,其功能是判断参数指定的年份是否为闰年,闰年时返回true,否则返回false。接下来的代码是判断指定年份中某个月份有多少天,其中,变量y为年份,变量m为月份,变量d为月份中有多少天。
if语句后的条件是当月份为1、3、5、7、8、10、12时,指定变量d为31。这里,首先使用“[1, 3, 5, 7, 8, 10, 12]”定义了包含大月数值的数组(Array)对象,然后使用数组对象的indexOf()方法判断变量m指定的月份是否在其中,如果在数组中找到变量m的值则返回其所在元素的索引值(大于或等于0的整数),条件正是通过返回的索引值来判断的。
第一个else if语句的条件中,当m指定的月份为2月时,则通过isLeap()函数判断变量y指定的年份是否为闰年。此外,这里还使用了?:运算符,其格式如下。
<表达式> ? <值1> : <值2>
这里,当<表达式>成立,即<表达式>结果为true时返回<值1>,否则返回<值2>。上述代码中,如果变量y指定的年份是闰年则返回29,否则返回28。
第二个else if语句的条件中,当变量m指定的是小月月份,则指定变量d的值为30;这里同样使用了数组元素的判断方法。请注意,由于变量d设置了初始的默认值-1,所以这里没有使用else语句设置月份不正确时的操作。
最后是显示结果,这里的if和else结构中分别只有一条语句,可以省略定义语句结构的花括号;此时,还可以将语句分别写在if和else语句的后面,如下面的代码。
// 显示结果 if (d > 0) alert(`${y}年${m}月有${d}天`); else alert("无效的日期信息");
从代码阅读的角度来,总是使用花括号定义语句结构,可以让代码结构更加清晰、可读性更强,对于代码的维护工作来说是不错的选择。
前面,获取指定年份中某个月份有多少天的示例中,条件的设置特点是只有一个变量m,即月份,然后针对变量m的值分别得到天数;像这样条件只有一个变量或表达式,并根据变量或表达式的值分别执行不同代码的情况,可以使用switch语句代替if语句,其应用结构如下。
switch(<表达式>) { case <值1>: <语句1> case <值2>: <语句2> case <值n>: <语句n> default: <语句n+1> }
结构中,当<表达式>的结果是<值1>时执行<语句1>,是<值2>时执行<语句2>,是<值n>时执行<语句n>;没有对应的值时执行<语句n+1>。这里,可以有一个或多个case语句,而default语句可有可无,需要注意的是,case和default结构都有向下贯穿的特性,先来看下面的代码。
<script> switch (1) { case 1: alert(1); default: alert("default"); case 2: alert(2); } </script>
代码中,switch语句的条件表达式为1,此时会显示1的对话框,然而,显示1之后并不会退出switch语句结构,而是继续向下执行default和其它case语句,又会显示"default"和2,这就是case和default语句的向下贯穿特性。此外,如代码中所示,default语句也可以不放在最后,但实际应用中,default语句一般会在最后,用于处理没有匹配值的情况。
在case或default结构中,如果代码执行完成后就需要退出switch语句,可以使用break语句。另一方面,合理利用向下贯穿特性也可以解决一些实际问题,如下面的代码就是使用switch语句重写获取指定年份某个月份的天数。
<script> // 判断年份是否为闰 function isLeap(y) { return y % 400 === 0 || (y % 100 !== 0 && y % 4 === 0); } // let y = 2020; let m = 9; let d = -1; // switch (m) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: d = 31; break; case 2: d = (isLeap(y) ? 29 : 28); break; case 4: case 6: case 9: case 11: d = 30; break; } // 显示结果 if (d > 0) alert(`${y}年${m}月有${d}天`); else alert("无效的日期信息"); </script>
switch语句中,通过case语句的向下贯穿特性,确定月份(变量m)为1、3、5、7、8、10、12时,将变量d的值设置为31,然后使用break语句退库switch结构;当月份为2时,通过?:运算符判断年份(变量y)是否为闰年,是闰年时2月为29天,否则为28天,并使用break语句退出switch结构;月份为4、6、9、11时,同样利用case语句的向下贯穿特性,设置变量d为30,虽然到“case 11:”时是最后一个case语句,但同样使用break语句确保退出switch结构是一个很好的编程习惯。
代码中,由于变量d设置了默认值-1,所在switch语句中没有使用default语句处理月份不正确的情况。