流程控制语句

本章将详细介绍ECMAScript中的流程控制语句,包括条件语句、循环语句和跳转语句,帮助您控制程序的执行流程。

7.1 流程控制概述

流程控制语句用于控制程序的执行顺序。默认情况下,程序按顺序执行语句,但通过流程控制语句可以改变执行顺序,实现条件执行、循环执行和跳转执行。

7.2 条件语句

条件语句根据条件的真假来决定是否执行特定代码块。

if语句

// 基本if语句
let age = 18;
if (age >= 18) {
    console.log("您已成年");
}

// if-else语句
if (age >= 18) {
    console.log("您已成年");
} else {
    console.log("您未成年");
}

// if-else if-else语句
if (age < 13) {
    console.log("儿童");
} else if (age < 20) {
    console.log("青少年");
} else if (age < 60) {
    console.log("成年人");
} else {
    console.log("老年人");
}

嵌套if语句

let score = 85;
let subject = "数学";

if (subject === "数学") {
    if (score >= 90) {
        console.log("数学优秀");
    } else if (score >= 80) {
        console.log("数学良好");
    } else {
        console.log("数学需要努力");
    }
}

条件(三元)运算符

// 简单条件赋值
let status = age >= 18 ? "成年人" : "未成年人";

// 复杂条件表达式
let message = score >= 90 ? "优秀" : 
              score >= 80 ? "良好" : 
              score >= 60 ? "及格" : "不及格";

7.3 switch语句

switch语句用于基于不同条件执行不同代码块。

基本switch语句

let day = 3;
let dayName;

switch (day) {
    case 1:
        dayName = "星期一";
        break;
    case 2:
        dayName = "星期二";
        break;
    case 3:
        dayName = "星期三";
        break;
    case 4:
        dayName = "星期四";
        break;
    case 5:
        dayName = "星期五";
        break;
    case 6:
        dayName = "星期六";
        break;
    case 7:
        dayName = "星期日";
        break;
    default:
        dayName = "无效的日期";
}

console.log(dayName); // "星期三"

fallthrough行为

// 利用fallthrough特性
let grade = 'B';
let comment;

switch (grade) {
    case 'A':
        comment = "优秀";
        // 注意:没有break语句
    case 'B':
        comment = "良好";
        // 注意:没有break语句
    case 'C':
        comment = "一般";
        break;
    default:
        comment = "需要改进";
}

console.log(comment); // "一般" (因为fallthrough)

7.4 循环语句

循环语句用于重复执行代码块。

for循环

// 基本for循环
for (let i = 0; i < 5; i++) {
    console.log(`第${i + 1}次循环`);
}

// 倒序循环
for (let i = 10; i >= 0; i--) {
    console.log(i);
}

// 循环数组
let fruits = ["苹果", "香蕉", "橙子"];
for (let i = 0; i < fruits.length; i++) {
    console.log(fruits[i]);
}

for-in循环

// 遍历对象属性
let person = {
    name: "张三",
    age: 25,
    city: "北京"
};

for (let key in person) {
    console.log(`${key}: ${person[key]}`);
}

// 遍历数组索引
let colors = ["红", "绿", "蓝"];
for (let index in colors) {
    console.log(`${index}: ${colors[index]}`);
}

for-of循环 (ES6)

// 遍历数组元素
let fruits = ["苹果", "香蕉", "橙子"];
for (let fruit of fruits) {
    console.log(fruit);
}

// 遍历字符串
let str = "Hello";
for (let char of str) {
    console.log(char);
}

// 遍历Set
let uniqueNumbers = new Set([1, 2, 3, 4, 5]);
for (let num of uniqueNumbers) {
    console.log(num);
}

// 遍历Map
let userRoles = new Map([
    ["张三", "管理员"],
    ["李四", "用户"],
    ["王五", "访客"]
]);
for (let [user, role] of userRoles) {
    console.log(`${user}: ${role}`);
}

while循环

// 基本while循环
let count = 0;
while (count < 5) {
    console.log(`计数: ${count}`);
    count++;
}

// 使用while处理未知次数的循环
let userInput;
while (userInput !== "退出") {
    userInput = prompt("输入'退出'结束程序");
    console.log(`您输入了: ${userInput}`);
}

do-while循环

// do-while循环至少执行一次
let password;
do {
    password = prompt("请输入密码");
} while (password !== "123456");

console.log("密码正确,登录成功");

7.5 跳转语句

跳转语句用于改变程序的正常执行流程。

break语句

// 在循环中使用break
for (let i = 0; i < 10; i++) {
    if (i === 5) {
        break; // 当i等于5时跳出循环
    }
    console.log(i); // 输出0, 1, 2, 3, 4
}

// 在switch中使用break
let command = "保存";
switch (command) {
    case "保存":
        console.log("正在保存文件...");
        break;
    case "打开":
        console.log("正在打开文件...");
        break;
    case "关闭":
        console.log("正在关闭文件...");
        break;
    default:
        console.log("未知命令");
}

continue语句

// 跳过循环中的某些迭代
for (let i = 0; i < 10; i++) {
    if (i % 2 === 0) {
        continue; // 跳过偶数
    }
    console.log(i); // 输出1, 3, 5, 7, 9
}

// 在while循环中使用continue
let num = 0;
while (num < 10) {
    num++;
    if (num % 3 === 0) {
        continue; // 跳过3的倍数
    }
    console.log(num);
}

标签语句

// 使用标签跳出嵌套循环
outer: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (i === 1 && j === 1) {
            break outer; // 跳出外层循环
        }
        console.log(`i=${i}, j=${j}`);
    }
}

// 使用标签继续外层循环
outer2: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (i === 1 && j === 1) {
            continue outer2; // 跳过外层循环的当前迭代
        }
        console.log(`i=${i}, j=${j}`);
    }
}

7.6 异常处理语句

异常处理语句用于处理程序运行时可能出现的错误。

try-catch语句

// 基本异常处理
try {
    let result = riskyOperation();
    console.log(result);
} catch (error) {
    console.log("发生错误:", error.message);
}

// 处理特定类型的错误
try {
    JSON.parse(invalidJsonString);
} catch (error) {
    if (error instanceof SyntaxError) {
        console.log("JSON格式错误");
    } else {
        console.log("其他错误:", error.message);
    }
}

finally语句

// finally块总是执行
try {
    console.log("尝试执行");
    throw new Error("出错了");
} catch (error) {
    console.log("捕获错误:", error.message);
} finally {
    console.log("清理资源");
}

// 无论是否发生异常,finally都会执行
function divide(a, b) {
    try {
        if (b === 0) {
            throw new Error("除数不能为零");
        }
        return a / b;
    } catch (error) {
        console.log("计算错误:", error.message);
        return null;
    } finally {
        console.log("计算完成");
    }
}

7.7 流程控制的最佳实践

避免深层嵌套

// 不推荐:深层嵌套
function processUser(user) {
    if (user) {
        if (user.isActive) {
            if (user.age >= 18) {
                if (user.hasPermission) {
                    // 处理用户
                }
            }
        }
    }
}

// 推荐:早期返回
function processUser(user) {
    if (!user) return;
    if (!user.isActive) return;
    if (user.age < 18) return;
    if (!user.hasPermission) return;
    
    // 处理用户
}

合理使用标签

// 在复杂的嵌套循环中使用标签提高可读性
processMatrix: for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
        if (matrix[i][j] === target) {
            console.log(`找到目标在位置(${i}, ${j})`);
            break processMatrix;
        }
    }
}

提示:合理使用流程控制语句可以让程序逻辑更清晰,但要避免过度复杂的嵌套结构。适当的函数拆分和早期返回可以让代码更易读和维护。