JavaScript比较运算、if与switch语句

编写逻辑更加复杂的代码时,会根据不同的条件来处理对应的代码,常用的编程要素包括本文介绍的比较运算、条件语句(if)、?:运算符、选择语句(switch),以及下一篇文章介绍的循环语句等语句结构。

比较运算

JavaScript中的比较运算包括:

  • 等于,使用==运算符。
  • 不等于,使用!=运算符。
  • 全等,使用===运算符。
  • 不全等,使用!==运算符。
  • 大于,使用>运算符。
  • 大于等于,使用>=运算符。
  • 小于,使用<运算符。
  • 小于等于,使用<=运算符。

比较运算会判断条件是否成立,成立时返回true,不成立时返回false。和日常的比较运算相比,JavaScript代码中需要注意相等的比较,特别是字符串和数值的比较;其中,等于运算(==)会判断数据的字符串形式是否相同,而全等运算(===)则判断数据的类型和值是否都相同。如下面的代码。

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中还需要注意浮点数的比较,先来看下面的代码。

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()方法,如下面的代码。

JavaScript
<script>
    let x = 0.1;
    let y = 10 % 3.3;
    alert(x.toFixed(4) === y.toFixed(4));  // true
</script>

代码中,变量x和y各保留4位小数,然后进行比较,结果为true。

下面的代码,创建了比较两个浮点数是否相等的函数。

JavaScript
<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语句结构可以根据不同的条件执行对应的代码,最简单的应用格式如下。

JavaScript
if (<条件>) {
    <语句>
}

结构中,当<条件>成立时执行<语句>,否则跳出if语句结构。如下面的代码演示了闰年的判断。

JavaScript
<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语句结构,应用格式如下。

JavaScript
if (<条件>) {
    <语句1>
} else {
    <语句2>
}

此结构中,当<条件>成立时,执行<语句1>,否则执行<语句2>;下面的代码演示了如何使用if...else语句判断整数是偶数还是奇数。

JavaScript
<script>
    let n = 9;
    if (n % 2 === 0) {
        alert(`${n}是偶数`)
    } else {
        alert(`${n}是奇数`)
    }
</script>

执行代码会显示“9是奇数”,可以修改变量n的值来观察运行结果。

如果有多个条件分支,还可以在if和else结构之间添加多个else if结构分别进行条件判断,应用结构如下。

JavaScript
if (<条件1>) {
    <语句1>
} else if (<条件2>) {
    <语句2>
} else if (<条件n>) {
    <语句n>
} else {
    <语句n+1>
}

此结构中,如果<条件1>成立则执行<语句1>,否则如果<条件2>成立则执行<语句2>,否则如果<条件n>成立则执行<语句n>;条件都不成立时执行<语句n+1>。下面的代码演示了相关应用。

JavaScript
<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指定的年份是否为闰年。此外,这里还使用了?:运算符,其格式如下。

JavaScript
<表达式> ? <值1> : <值2>

这里,当<表达式>成立,即<表达式>结果为true时返回<值1>,否则返回<值2>。上述代码中,如果变量y指定的年份是闰年则返回29,否则返回28。

第二个else if语句的条件中,当变量m指定的是小月月份,则指定变量d的值为30;这里同样使用了数组元素的判断方法。请注意,由于变量d设置了初始的默认值-1,所以这里没有使用else语句设置月份不正确时的操作。

最后是显示结果,这里的if和else结构中分别只有一条语句,可以省略定义语句结构的花括号;此时,还可以将语句分别写在if和else语句的后面,如下面的代码。

JavaScript
// 显示结果
if (d > 0) alert(`${y}年${m}月有${d}天`);
else alert("无效的日期信息");

从代码阅读的角度来,总是使用花括号定义语句结构,可以让代码结构更加清晰、可读性更强,对于代码的维护工作来说是不错的选择。

switch语句

前面,获取指定年份中某个月份有多少天的示例中,条件的设置特点是只有一个变量m,即月份,然后针对变量m的值分别得到天数;像这样条件只有一个变量或表达式,并根据变量或表达式的值分别执行不同代码的情况,可以使用switch语句代替if语句,其应用结构如下。

JavaScript
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结构都有向下贯穿的特性,先来看下面的代码。

JavaScript
<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语句重写获取指定年份某个月份的天数。

JavaScript
<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语句处理月份不正确的情况。