Skip to content

流程控制

流程控制语句用于控制代码的执行顺序。JavaScript 提供了条件语句和循环语句来实现不同的控制流程。

条件语句

条件语句根据条件决定执行哪些代码。

if 语句

javascript
// 基本 if 语句
let age = 18;

if (age >= 18) {
    console.log('已成年');
}

// if...else 语句
let score = 55;

if (score >= 60) {
    console.log('及格');
} else {
    console.log('不及格');
}

// if...else if...else 语句
let grade = 85;

if (grade >= 90) {
    console.log('优秀');
} else if (grade >= 80) {
    console.log('良好');
} else if (grade >= 70) {
    console.log('中等');
} else if (grade >= 60) {
    console.log('及格');
} else {
    console.log('不及格');
}

// 条件可以是任何表达式
// 假值:false, 0, '', null, undefined, NaN
let value = 'hello';

if (value) {
    console.log('value 是真值');  // 执行
}

let emptyStr = '';
if (emptyStr) {
    console.log('不会执行');
}

// 嵌套 if 语句
let hasTicket = true;
let age2 = 20;

if (hasTicket) {
    if (age2 >= 18) {
        console.log('可以进入');
    } else {
        console.log('未成年人禁止入内');
    }
} else {
    console.log('请先购票');
}

// 代码块只有一条语句时可省略花括号(不推荐)
if (age >= 18) console.log('已成年');

// 推荐始终使用花括号
if (age >= 18) {
    console.log('已成年');
}

switch 语句

javascript
// switch 语句用于多分支选择
let day = 3;
let dayName;

switch (day) {
    case 1:
        dayName = '星期一';
        break;  // 必须使用 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);  // 星期三

// 没有 break 的情况(穿透)
let num = 2;

switch (num) {
    case 1:
        console.log('一');
        // 没有 break,会继续执行下一个 case
    case 2:
        console.log('二');
    case 3:
        console.log('三');
        break;  // 在这里停止
    default:
        console.log('其他');
}
// 输出:二、三

// 利用穿透特性
let month = 2;

switch (month) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        console.log('31天');
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        console.log('30天');
        break;
    case 2:
        console.log('28或29天');
        break;
    default:
        console.log('无效月份');
}

// switch 使用严格相等比较
let x = '10';

switch (x) {
    case 10:
        console.log('数字 10');
        break;
    case '10':
        console.log('字符串 10');  // 执行这个
        break;
}

// switch 表达式(ES2020+)
// 可以在 switch 中使用表达式
let a = 1, b = 2;

switch (true) {
    case a > b:
        console.log('a > b');
        break;
    case a < b:
        console.log('a < b');  // 执行
        break;
    default:
        console.log('a = b');
}

条件运算符

javascript
// 三元运算符:条件 ? 表达式1 : 表达式2
let age = 20;
let status = age >= 18 ? '成年' : '未成年';
console.log(status);  // 成年

// 嵌套三元运算符
let score = 75;
let grade = score >= 90 ? 'A' :
            score >= 80 ? 'B' :
            score >= 70 ? 'C' :
            score >= 60 ? 'D' : 'F';
console.log(grade);  // C

// 实际应用
let user = null;
let userName = user ? user.name : '游客';
console.log(userName);  // 游客

// 设置默认值
let input = '';
let value = input ? input : '默认值';
console.log(value);  // 默认值

循环语句

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

while 循环

javascript
// while 循环:先判断条件,再执行循环体
let i = 0;

while (i < 5) {
    console.log(i);  // 输出 0, 1, 2, 3, 4
    i++;
}

// 注意:必须确保循环条件最终变为 false,否则会无限循环
// let j = 0;
// while (j < 5) {
//     console.log(j);
//     // 忘记 j++,导致无限循环
// }

// 计算 1 到 100 的和
let sum = 0;
let n = 1;
while (n <= 100) {
    sum += n;
    n++;
}
console.log(sum);  // 5050

// 使用 break 跳出循环
let count = 0;
while (true) {  // 无限循环
    console.log(count);
    count++;
    if (count >= 5) {
        break;  // 跳出循环
    }
}

// 使用 continue 跳过当前迭代
let m = 0;
while (m < 10) {
    m++;
    if (m % 2 === 0) {
        continue;  // 跳过偶数
    }
    console.log(m);  // 输出奇数:1, 3, 5, 7, 9
}

do...while 循环

javascript
// do...while 循环:先执行循环体,再判断条件
// 至少执行一次

let i = 0;
do {
    console.log(i);  // 输出 0, 1, 2, 3, 4
    i++;
} while (i < 5);

// 即使条件一开始就不满足,也会执行一次
let j = 10;
do {
    console.log(j);  // 输出 10
    j++;
} while (j < 5);

// 实际应用:获取用户输入直到有效
let input;
do {
    input = prompt('请输入一个正数:');
} while (isNaN(input) || Number(input) <= 0);

// 猜数字游戏
let target = Math.floor(Math.random() * 100) + 1;
let guess;
do {
    guess = Number(prompt('猜一个 1-100 的数字:'));
    if (guess < target) {
        alert('太小了,再试试!');
    } else if (guess > target) {
        alert('太大了,再试试!');
    }
} while (guess !== target);
alert('恭喜你猜对了!');

for 循环

javascript
// for 循环:最常用的循环
// for (初始化; 条件; 更新) { 循环体 }

// 基本用法
for (let i = 0; i < 5; i++) {
    console.log(i);  // 输出 0, 1, 2, 3, 4
}

// for 循环的执行顺序
// 1. 初始化(let i = 0)- 只执行一次
// 2. 条件判断(i < 5)- 为 true 继续执行
// 3. 执行循环体
// 4. 更新(i++)
// 5. 回到第 2 步

// 计算 1 到 100 的和
let sum = 0;
for (let i = 1; i <= 100; i++) {
    sum += i;
}
console.log(sum);  // 5050

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

// 倒序遍历
for (let i = fruits.length - 1; i >= 0; i--) {
    console.log(fruits[i]);
}

// 省略部分表达式
let i = 0;
for (; i < 5; i++) {  // 省略初始化
    console.log(i);
}

for (let j = 0; ; j++) {  // 省略条件(无限循环)
    console.log(j);
    if (j >= 5) break;
}

for (;;) {  // 省略所有(无限循环)
    console.log('无限循环');
    break;
}

// 多个变量
for (let i = 0, j = 10; i < j; i++, j--) {
    console.log(i, j);
}

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

for (let i = 0; i < 10; i++) {
    if (i % 2 === 0) {
        continue;  // 跳过偶数
    }
    console.log(i);  // 输出奇数:1, 3, 5, 7, 9
}

for...in 循环

javascript
// for...in 遍历对象的可枚举属性
let person = {
    name: '张三',
    age: 25,
    city: '北京'
};

for (let key in person) {
    console.log(key + ': ' + person[key]);
}
// 输出:
// name: 张三
// age: 25
// city: 北京

// 遍历数组(不推荐)
let arr = ['a', 'b', 'c'];
for (let index in arr) {
    console.log(index, arr[index]);
}
// 输出:0 a, 1 b, 2 c
// 注意:index 是字符串,不是数字

// 遍历数组推荐使用 for...of 或普通 for 循环

// for...in 会遍历继承的属性
let obj = Object.create({ inherited: '继承属性' });
obj.own = '自身属性';

for (let key in obj) {
    console.log(key);  // own, inherited
}

// 只遍历自身属性
for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
        console.log(key);  // own
    }
}

for...of 循环(ES6)

javascript
// for...of 遍历可迭代对象(数组、字符串、Map、Set 等)
let arr = ['a', 'b', 'c'];

for (let value of arr) {
    console.log(value);  // 输出 a, b, c
}

// 遍历字符串
let str = '你好';
for (let char of str) {
    console.log(char);  // 输出 你, 好
}

// 遍历 Map
let map = new Map([
    ['name', '张三'],
    ['age', 25]
]);
for (let [key, value] of map) {
    console.log(key, value);
}

// 遍历 Set
let set = new Set([1, 2, 3, 3]);
for (let value of set) {
    console.log(value);  // 输出 1, 2, 3
}

// 获取索引
for (let [index, value] of arr.entries()) {
    console.log(index, value);
}

// for...of 与 for...in 的区别
let arr2 = ['a', 'b', 'c'];
arr2.foo = 'bar';

for (let key in arr2) {
    console.log(key);  // 0, 1, 2, foo
}

for (let value of arr2) {
    console.log(value);  // a, b, c
}

循环嵌套

javascript
// 嵌套循环
for (let i = 1; i <= 3; i++) {
    for (let j = 1; j <= 3; j++) {
        console.log(`i=${i}, j=${j}`);
    }
}

// 输出乘法表
for (let i = 1; i <= 9; i++) {
    let line = '';
    for (let j = 1; j <= i; j++) {
        line += `${j}×${i}=${i * j}\t`;
    }
    console.log(line);
}

// 输出三角形
for (let i = 1; i <= 5; i++) {
    let line = '';
    for (let j = 1; j <= i; j++) {
        line += '*';
    }
    console.log(line);
}

// 输出金字塔
let n = 5;
for (let i = 1; i <= n; i++) {
    let line = '';
    // 空格
    for (let j = 1; j <= n - i; j++) {
        line += ' ';
    }
    // 星号
    for (let k = 1; k <= 2 * i - 1; k++) {
        line += '*';
    }
    console.log(line);
}

// break 和 continue 在嵌套循环中
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}`);
    }
}

跳转语句

break 语句

javascript
// break 跳出循环
for (let i = 0; i < 10; i++) {
    if (i === 5) {
        break;  // 跳出循环
    }
    console.log(i);  // 输出 0, 1, 2, 3, 4
}

// break 在 switch 中
let day = 2;
switch (day) {
    case 1:
        console.log('星期一');
        break;
    case 2:
        console.log('星期二');
        break;
    default:
        console.log('其他');
}

// 带标签的 break
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}, ${j})`);
    }
}

// 查找数组中的元素
let arr = [1, 2, 3, 4, 5];
let target = 3;
let found = false;

for (let i = 0; i < arr.length; i++) {
    if (arr[i] === target) {
        found = true;
        break;  // 找到后跳出循环
    }
}

console.log(found ? '找到了' : '未找到');

continue 语句

javascript
// continue 跳过当前迭代,继续下一次
for (let i = 0; i < 10; i++) {
    if (i % 2 === 0) {
        continue;  // 跳过偶数
    }
    console.log(i);  // 输出奇数:1, 3, 5, 7, 9
}

// 过滤数组中的元素
let arr = [1, -2, 3, -4, 5, -6];
let positives = [];

for (let num of arr) {
    if (num < 0) {
        continue;  // 跳过负数
    }
    positives.push(num);
}
console.log(positives);  // [1, 3, 5]

// 带标签的 continue
outer: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (i === j) {
            continue outer;  // 跳到外层循环的下一次迭代
        }
        console.log(`(${i}, ${j})`);
    }
}
// 输出:(0,1), (0,2), (1,0), (1,2), (2,0), (2,1)

return 语句

javascript
// return 用于函数中,返回值并结束函数
function add(a, b) {
    return a + b;  // 返回结果
}

let result = add(1, 2);
console.log(result);  // 3

// return 提前结束函数
function divide(a, b) {
    if (b === 0) {
        return '除数不能为 0';  // 提前返回
    }
    return a / b;
}

console.log(divide(10, 2));  // 5
console.log(divide(10, 0));  // 除数不能为 0

// 没有返回值的函数返回 undefined
function greet(name) {
    console.log('Hello, ' + name);
    // 没有 return 语句
}

let value = greet('张三');  // 输出:Hello, 张三
console.log(value);  // undefined

// return 单独使用
function checkAge(age) {
    if (age < 0) {
        return;  // 提前结束,返回 undefined
    }
    console.log('年龄有效');
}

异常处理

try...catch...finally

javascript
// try...catch 捕获异常
try {
    // 可能出错的代码
    let result = someUndefinedVariable;  // 引用错误
} catch (error) {
    // 处理错误
    console.log('发生错误:' + error.message);
}

// try...catch...finally
try {
    console.log('尝试执行');
    throw new Error('自定义错误');  // 抛出错误
} catch (error) {
    console.log('捕获错误:' + error.message);
} finally {
    console.log('无论如何都会执行');  // 始终执行
}

// 实际应用:解析 JSON
function parseJSON(str) {
    try {
        return JSON.parse(str);
    } catch (error) {
        console.log('JSON 解析失败:' + error.message);
        return null;
    }
}

let data = parseJSON('{"name": "张三"}');
console.log(data);  // { name: '张三' }

let invalidData = parseJSON('invalid json');
console.log(invalidData);  // null

// 获取错误信息
try {
    JSON.parse('invalid');
} catch (error) {
    console.log(error.name);     // SyntaxError
    console.log(error.message);  // 错误信息
    console.log(error.stack);    // 错误堆栈
}

throw 语句

javascript
// throw 抛出异常
function divide(a, b) {
    if (b === 0) {
        throw new Error('除数不能为 0');
    }
    return a / b;
}

try {
    divide(10, 0);
} catch (error) {
    console.log(error.message);  // 除数不能为 0
}

// 抛出不同类型的错误
function validateAge(age) {
    if (typeof age !== 'number') {
        throw new TypeError('年龄必须是数字');
    }
    if (age < 0 || age > 150) {
        throw new RangeError('年龄必须在 0-150 之间');
    }
    return true;
}

try {
    validateAge('abc');
} catch (error) {
    console.log(error.name);    // TypeError
    console.log(error.message); // 年龄必须是数字
}

// 自定义错误类型
class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = 'ValidationError';
    }
}

function validateUser(user) {
    if (!user.name) {
        throw new ValidationError('用户名不能为空');
    }
    if (!user.email) {
        throw new ValidationError('邮箱不能为空');
    }
    return true;
}

try {
    validateUser({ name: '' });
} catch (error) {
    if (error instanceof ValidationError) {
        console.log('验证错误:' + error.message);
    } else {
        console.log('其他错误:' + error.message);
    }
}

// 抛出任意值(不推荐)
throw '字符串错误';  // 可以,但不推荐
throw 123;          // 可以,但不推荐
throw { message: '错误对象' };  // 可以,但不推荐

// 推荐使用 Error 对象
throw new Error('错误信息');

实战案例

成绩等级判断

javascript
function getGrade(score) {
    // 验证输入
    if (typeof score !== 'number' || isNaN(score)) {
        throw new TypeError('成绩必须是数字');
    }
    if (score < 0 || score > 100) {
        throw new RangeError('成绩必须在 0-100 之间');
    }
    
    // 判断等级
    if (score >= 90) return 'A';
    if (score >= 80) return 'B';
    if (score >= 70) return 'C';
    if (score >= 60) return 'D';
    return 'F';
}

// 测试
try {
    console.log(getGrade(95));  // A
    console.log(getGrade(72));  // C
    console.log(getGrade(55));  // F
    console.log(getGrade(150)); // 抛出错误
} catch (error) {
    console.log('错误:' + error.message);
}

计算器

javascript
function calculator(num1, operator, num2) {
    // 转换为数字
    num1 = Number(num1);
    num2 = Number(num2);
    
    // 验证输入
    if (isNaN(num1) || isNaN(num2)) {
        throw new Error('操作数必须是数字');
    }
    
    // 根据运算符计算
    switch (operator) {
        case '+':
            return num1 + num2;
        case '-':
            return num1 - num2;
        case '*':
            return num1 * num2;
        case '/':
            if (num2 === 0) {
                throw new Error('除数不能为 0');
            }
            return num1 / num2;
        case '%':
            return num1 % num2;
        case '**':
            return num1 ** num2;
        default:
            throw new Error('不支持的运算符:' + operator);
    }
}

// 测试
try {
    console.log(calculator(10, '+', 5));   // 15
    console.log(calculator(10, '/', 2));   // 5
    console.log(calculator(2, '**', 3));   // 8
    console.log(calculator(10, '/', 0));   // 抛出错误
} catch (error) {
    console.log('计算错误:' + error.message);
}

猜数字游戏

javascript
function guessNumberGame() {
    // 生成随机数
    const target = Math.floor(Math.random() * 100) + 1;
    let attempts = 0;
    let maxAttempts = 10;
    
    console.log('猜数字游戏开始!范围:1-100');
    
    while (attempts < maxAttempts) {
        // 获取用户输入(实际应用中可使用 prompt)
        // 这里模拟输入
        let guess = Math.floor(Math.random() * 100) + 1;
        attempts++;
        
        console.log(`第 ${attempts} 次猜测:${guess}`);
        
        if (guess === target) {
            console.log('恭喜你猜对了!');
            return;
        } else if (guess < target) {
            console.log('太小了!');
        } else {
            console.log('太大了!');
        }
    }
    
    console.log(`游戏结束!正确答案是 ${target}`);
}

guessNumberGame();

打印图案

javascript
// 打印空心正方形
function printHollowSquare(n) {
    for (let i = 0; i < n; i++) {
        let line = '';
        for (let j = 0; j < n; j++) {
            // 边界位置打印 *,内部打印空格
            if (i === 0 || i === n - 1 || j === 0 || j === n - 1) {
                line += '* ';
            } else {
                line += '  ';
            }
        }
        console.log(line);
    }
}

printHollowSquare(5);

// 打印菱形
function printDiamond(n) {
    // 上半部分
    for (let i = 1; i <= n; i++) {
        let line = '';
        // 空格
        for (let j = 1; j <= n - i; j++) {
            line += ' ';
        }
        // 星号
        for (let k = 1; k <= 2 * i - 1; k++) {
            line += '*';
        }
        console.log(line);
    }
    // 下半部分
    for (let i = n - 1; i >= 1; i--) {
        let line = '';
        // 空格
        for (let j = 1; j <= n - i; j++) {
            line += ' ';
        }
        // 星号
        for (let k = 1; k <= 2 * i - 1; k++) {
            line += '*';
        }
        console.log(line);
    }
}

printDiamond(5);

小结

本章学习了 JavaScript 的流程控制:

  • 条件语句:if、if...else、if...else if...else、switch
  • 循环语句:while、do...while、for、for...in、for...of
  • 跳转语句:break、continue、return
  • 异常处理:try...catch...finally、throw

下一章我们将学习 函数,了解如何封装可重用的代码块。