零基础鸿蒙应用开发第七节:流程控制之分支语句

零基础鸿蒙应用开发学习计划表

【学习目标】

  1. 理解流程控制的核心作用,掌握分支语句的本质(根据条件执行不同逻辑)
  2. 掌握if-else分支语句的基础语法、嵌套用法及边界条件处理
  3. 掌握switch语句的语法规则、break关键字的作用,理解穿透特性的使用场景
  4. 掌握三元运算符的语法与简写逻辑,区分其与if-else的适用场景
  5. 能结合实际业务场景(成绩等级判断、订单状态处理、登录验证)选择合适的分支语句

【学习重点】

  1. if-else的条件表达式结果必须是布尔值。
  2. switch语句中break不可省略(否则会触发穿透),default分支用于处理异常情况
  3. 三元运算符仅适用于“二选一”的简单场景,复杂逻辑优先用if-else
  4. 嵌套if-else需控制层级(建议不超过3层),提升代码可读性
  5. 明确三者适用场景:简单二选一用三元运算符,多条件等值判断用switch,复杂条件判断用if-else

一、工程结构

本节我们将创建名为FlowControlBranchDemo的工程,基于鸿蒙5.0(API12)开发,使用DevEco Studio 6.0+工具,项目结构目录如下:

FlowControlBranchDemo
├── AppScope                 # 应用全局配置
├── entry                    # 主模块目录
│   ├── src
│   │   ├── main
│   │   │   ├── ets          # ArkTS核心代码目录
│   │   │   │   ├── entryability   # 应用入口能力
│   │   │   │   ├── pages          # 页面组件目录
│   │   │   │   │   └── Index.ets  # 核心页面(调用测试函数)
│   │   │   │   └── utils          # 工具函数目录
│   │   │   │       └── FlowControlTest.ets # 本节所有学习内容
│   │   │   ├── resources        # 静态资源(图片/字符串等)
│   │   │   └── module.json5     # 模块配置文件
│   │   ├── ohosTest         # 鸿蒙测试目录
│   │   └── test             # 单元测试目录
│   ├── build-profile.json5  # 构建配置
│   ├── hvigorfile.ts        # 构建脚本
│   └── oh-package.json5     # 依赖配置
├── hvigor                   # 构建工具依赖
└── oh_modules               # 第三方依赖包

开发准备步骤

  1. 打开DevEco Studio,创建FlowControlBranchDemo项目(选择Empty Ability模板,API12/鸿蒙5.0);
  2. src/main/ets目录下右键创建utils文件夹;
  3. 选中utils文件夹,右键创建FlowControlTest.ets文件(封装所有分支语句测试函数);
  4. 所有学习内容在Index.etsaboutToAppear生命周期中统一调用,无需额外配置。

二、流程控制核心概念:为什么需要分支语句?

在编程中,流程控制用于控制代码的执行顺序,而分支语句是流程控制的核心之一,解决“根据不同条件执行不同逻辑”的问题。

举个生活例子:

  • 出门前看天气:如果下雨,带雨伞;否则,带太阳镜(二选一逻辑);
  • 成绩评级:90分以上为A,80-89分为B,70-79分为C,否则为D(多条件逻辑);
  • 订单支付:待支付→显示“支付按钮”,已支付→显示“待发货”,已取消→显示“重新下单”(多状态逻辑)。

这些场景都需要“根据条件做选择”,而分支语句(if-else/switch/三元运算符)就是编程中的“选择工具”。

三、if-else语句:复杂条件的灵活判断

if-else是最常用的分支语句,支持任意布尔条件判断,灵活应对简单到复杂的逻辑场景。

3.1 基础语法(二选一逻辑)

语法结构

if (条件表达式) {
  // 条件为true时执行的代码块
} else {
  // 条件为false时执行的代码块
}
  • 条件表达式必须返回boolean类型(true/false),直接用数字、字符串等非布尔值不会编译报错,但会触发隐式类型转换,极易导致逻辑错误;
  • 代码块{}:当只有一行代码时可省略,但推荐始终保留(提升可读性,避免后续扩展出错)。

代码示例:基础if-else(成绩及格判断)

/**
 * 测试基础if-else:成绩及格判断(二选一逻辑)
 * 覆盖场景:常规及格分数、常规不及格分数,重点验证if/else两个分支的执行效果
 */
export function testIfElseBasic(): void {
  console.log(`\n========== 基础if-else(成绩及格判断) ==========`);
  
  // 场景1:常规及格分数(触发if分支)
  const score: number = 85; 
  if (score >= 60) {
    console.log(`分数:${score},结果:及格`);
  } else {
    console.log(`分数:${score},结果:不及格`);
  }

  // 场景2:常规不及格分数(触发else分支,对比边界值60分的核心场景)
  const score2: number = 59; 
  if (score2 >= 60) {
    console.log(`分数:${score2},结果:及格`);
  } else {
    console.log(`分数:${score2},结果:不及格`);
  }

  // 判断条件要严谨 如果我们只写 if (score > 60) 就会漏掉刚刚及格的60分

}

运行效果

========== 基础if-else(成绩及格判断) ==========
分数:85,结果:及格
分数:59,结果:不及格

3.2 多条件分支(if-else if-else)

当需要判断多个连续条件时,使用if-else if-else结构,按顺序执行条件判断,直到找到第一个为true的条件。

语法结构

if (条件1) {
  // 条件1为true时执行
} else if (条件2) {
  // 条件1为false,条件2为true时执行
} else if (条件3) {
  // 条件1、2为false,条件3为true时执行
} else {
  // 所有条件都为false时执行(可选)
}

代码示例:多条件if-else if-else(成绩等级判断)

/**
 * 测试多条件if-else if-else:成绩等级判断(A/B/C/D)
 */
export function testIfElseMulti(): void {
  console.log(`\n========== 多条件if-else if-else(成绩等级判断) ==========`);
  const score: number = 78; 

  // 条件顺序:从高到低(需注意:顺序颠倒会导致逻辑错误)
  if (score >= 90) {
    console.log(`分数:${score},等级:A(优秀)`);
  } else if (score >= 80) {
    console.log(`分数:${score},等级:B(良好)`);
  } else if (score >= 60) {
    console.log(`分数:${score},等级:C(及格)`);
  } else {
    console.log(`分数:${score},等级:D(不及格)`);
  }

  // 错误示例(注释掉,可取消注释验证问题)
  // const wrongScore: number = 85; // 补全显式类型标注
  // if (wrongScore >= 60) { // 第一个条件就满足,后续条件不会执行
  //   console.log(`分数:${wrongScore},等级:C`); // 错误结果
  // } else if (wrongScore >= 80) {
  //   console.log(`分数:${wrongScore},等级:B`);
  // }
}

运行效果

========== 多条件if-else if-else(成绩等级判断) ==========
分数:78,等级:C(及格)

3.3 嵌套if-else(复杂逻辑分层判断)

当条件需要分层判断时(比如“先判断是否获取授权,再判断是否账号密码”),可以在ifelse代码块中嵌套另一个if-else

代码示例:嵌套if-else(登录验证逻辑)

/**
 * 测试嵌套if-else:登录验证(先判断账号,再判断密码)
 */
export function testIfElseNested(): void {
  console.log(`\n========== 嵌套if-else(授权登录验证) ==========`);
  const isAuthorized: boolean = true; // 是否拥有登录授权(核心前置条件)
  const username: string = "admin"; 
  const password: string = "123456"; 

  // 第一层:判断是否获得授权
  if (isAuthorized === true) {
    console.log("授权验证通过");
    // 第二层:判断账号密码是否正确(嵌套层级≤3层)
    if  (username === "admin" && password === "123456") {
      console.log("账号密码验证通过,登录成功!");
    } else {
      console.log("账号或密码错误,登录失败");
    }
  } else {
    console.log("未获得授权");
  }
}

运行效果

========== 嵌套if-else(授权登录验证) ==========
授权验证通过
账号密码验证通过,登录成功!

代码示例:if-else(布尔值隐式转换场景)

/**
 * 测试if-else布尔值隐式转换:结合Boolean内置对象的空值判断
 * 重点:展示非布尔值在条件语句中的隐式转换规则
 */
export function testIfElseNullCheck(): void { 
  console.log(`\n========== if-else(布尔值隐式转换) ==========`);
  const username: string|null = "admin"; 
  const password: string|null = null; 

  // 演示:非布尔值作为条件的隐式转换
  if (username) {
    console.log(`用户名:${username} → 非空值,隐式转换为true`);
  } else {
    console.log("用户名是空值,隐式转换为false");
  }

  if (password) {
    console.log(`密码:${password} → 非空值,隐式转换为true`);
  } else {
    console.log(`密码:${password} → 空值,隐式转换为false`);
  }

  // 显式判断(推荐写法,规避隐式转换坑)
  if (username !== "" && username !== null) {
    console.log("显式判断:用户名非空且有效");
  }
}

运行效果

========== if-else(布尔值隐式转换) ==========
用户名:admin → 非空值,隐式转换为true
密码:null → 空值,隐式转换为false
显式判断:用户名非空且有效

3.4 if-else易错点

  1. 条件表达式非布尔值(隐式转换坑)
    • 非空字符串(如"admin")、非0数字(如50)会隐式转为true,空字符串""nullundefined、0会隐式转为false
    • 错误示例:用if (score)判断50分是否及格(50是非0数字→true,误判为及格),正确应写if (score >= 60)
    • 典型场景:if (username)仅能判断是否为null/空字符串,但无法区分"0"/空格等无效值,需显式判断。
  2. 多条件顺序颠倒:多条件if-else if必须按“范围从大到小”或“优先级从高到低”排序(如先判断90分以上,再判断80分以上),否则会出现逻辑错误;
  3. 嵌套层级过多:超过3层嵌套会导致代码“臃肿”,后续可通过函数拆分优化;
  4. 遗漏边界条件:比如忘记判断score === 60(刚好及格)、username === ""(空字符串),需覆盖所有可能的情况。

四、switch语句:多状态的等值判断

switch语句适用于多个等值条件判断(比如订单状态、性别、月份等),语法比多条件if-else更简洁,可读性更强。

4.0 全局枚举定义

// 定义订单状态枚举(全局作用域,语义化,避免魔法字符串)
enum OrderStatus {
  Pending = "待支付",
  Paid = "已支付",
  Canceled = "已取消"
}

// 定义月份枚举(全局作用域,语义化命名,避免数字硬编码)
enum MonthEnum {
  Jan = "1月",
  Feb = "2月",
  Mar = "3月"
}

4.1 基础语法

switch (判断值) {
  case 值1:
    // 判断值 === 值1 时执行
    break; // 必须加break,否则会穿透到下一个case
  case 值2:
    // 判断值 === 值2 时执行
    break;
  case 值3:
    // 判断值 === 值3 时执行
    break;
  default:
    // 所有case都不匹配时执行(可选,建议保留)
    break;
}
  • 判断值类型:支持numberstringenum(枚举)等基本类型,不支持对象;
  • 类型规范:判断值需显式标注类型(禁止隐式类型推断),且判断值类型需与case值类型严格一致(ArkTS 用===严格相等判断);
  • 等值判断:使用===严格相等(值和类型都必须匹配);
  • break关键字:用于终止switch语句,避免“穿透效应”(即执行完当前case后,继续执行下一个case);
  • default分支:用于处理所有case之外的异常情况,提升代码健壮性。

代码示例:基础switch(订单状态处理)

/**
 * 测试基础switch:订单状态处理(多等值条件)
 */
export function testSwitchBasic(): void {
  console.log(`\n========== 基础switch(订单状态处理) ==========`);
  const orderStatus: string = "Paid"; 

  switch (orderStatus) {
    case "Pending":
      console.log("订单状态:待支付,提示用户尽快完成支付");
      break; // 终止当前case,避免穿透
    case "Paid":
      console.log("订单状态:已支付,系统将在24小时内发货");
      break;
    case "Canceled":
      console.log("订单状态:已取消,可重新下单");
      break;
    default:
      console.log("订单状态:未知,建议联系客服核实");
      break;
  }
}

运行效果

========== 基础switch(订单状态处理) ==========
订单状态:已支付,系统将在24小时内发货

4.2 switch的穿透效应(fallthrough)

case中没有break时,会触发“穿透效应”——执行完当前case后,继续执行下一个case的代码,直到遇到breakswitch结束。

提示:穿透不是bug,而是特性,适用于“多个case共享同一逻辑”的场景。

代码示例:穿透效应(月份季节判断)

/**
 * 测试switch穿透效应:月份对应季节(多个case对应同一逻辑)
 */
export function testSwitchFallthrough(): void {
  console.log(`\n========== switch穿透效应(月份季节判断) ==========`);
  // 推荐显式标注类型:避免隐式推断导致case值类型不匹配
  const month: number = 3; 

  switch (month) {
    // 12/1/2月都属于冬季(穿透共享逻辑)
    case 12:
    case 1:
    case 2:
      console.log(`月份:${month},季节:冬季`);
      break;
    // 3/4/5月都属于春季
    case 3:
    case 4:
    case 5:
      console.log(`月份:${month},季节:春季`);
      break;
    // 6/7/8月都属于夏季
    case 6:
    case 7:
    case 8:
      console.log(`月份:${month},季节:夏季`);
      break;
    // 9/10/11月都属于秋季
    case 9:
    case 10:
    case 11:
      console.log(`月份:${month},季节:秋季`);
      break;
    default:
      console.log(`月份:${month},无效月份`);
      break;
  }
}

运行效果

========== switch穿透效应(月份季节判断) ==========
月份:3,季节:春季

4.3 switch与枚举结合(最佳实践)

代码示例:switch+枚举(订单状态优化)

/**
 * 测试switch+枚举:订单状态处理(最佳实践)
 */
export function testSwitchWithEnum_Order(currentStatus: OrderStatus): void {
  console.log(`\n========== switch+枚举(订单状态优化) ==========`);
  switch (currentStatus) {
    case OrderStatus.Pending:
      console.log(`订单状态:${currentStatus},提示用户尽快完成支付`);
      break;
    case OrderStatus.Paid:
      console.log(`订单状态:${currentStatus},系统将在24小时内发货`);
      break;
    case OrderStatus.Canceled:
      console.log(`订单状态:${currentStatus},可重新下单`);
      break;
    default:
      console.log(`订单状态:未知,建议联系客服核实`);
      break;
  }
}

运行效果

========== switch+枚举(订单状态优化) ==========
订单状态:待支付,提示用户尽快完成支付

代码示例2:switch+枚举(月份场景,优化穿透效应)

/**
 * 测试switch+枚举:月份判断(穿透效应+枚举最佳实践)
 */
export function testSwitchWithEnum_Month(currentMonth: MonthEnum): void {
  console.log(`\n========== switch+枚举(月份判断) ==========`);
  switch (currentMonth) {
    // 1月/2月共享冬季逻辑(利用穿透效应简化代码)
    case MonthEnum.Jan:
    case MonthEnum.Feb:
      console.log(`当前月份:${currentMonth},属于冬季`);
      break;
    case MonthEnum.Mar:
      console.log(`当前月份:${currentMonth},属于春季`);
      break;
    default:
      console.log(`当前月份:${currentMonth},无效月份`);
      break;
  }
}

运行效果

========== switch+枚举(月份判断) ==========
当前月份:2月,属于冬季

4.4 switch易错点

  1. 遗漏break:这是最常见错误,会导致穿透效应,除非主动需要穿透,否则每个case都必须加break
  2. 使用非等值条件:switch只能判断“是否相等”,不能判断范围(比如score >= 90),范围判断需用if-else;
  3. 判断值类型不匹配:比如switch (123)case "123",因类型不同不会匹配,会执行default;
  4. 忽略default分支:建议始终保留default,处理异常情况(比如用户输入无效值);
  5. 枚举作用域错误:枚举定义在函数内会导致外部调用时“未定义”,需定义在全局作用域;
  6. 隐式类型推断:判断值未显式标注类型(如const month = 3;),易导致与case值类型不匹配。

五、三元运算符:二选一的简洁写法

三元运算符是if-else简写形式,仅适用于“二选一”的简单逻辑,语法简洁,一行代码即可完成判断。

5.1 基础语法

条件表达式 ? 条件为true时的返回值 : 条件为false时的返回值
  • 条件表达式必须返回boolean类型;
  • 返回值可以是任意类型(数字、字符串、变量、甚至函数调用);
  • 优先级较低,复杂场景建议用括号包裹(比如结合其他运算符时)。

代码示例:基础三元运算符(简单二选一)

/**
 * 测试基础三元运算符:简单二选一逻辑
 */
export function testTernaryBasic(): void {
  console.log(`\n========== 基础三元运算符(简单二选一) ==========`);
  // 场景1:判断年龄是否成年
  const age: number = 20; // 显式标注数字类型
  const adultStatus: string = age >= 18 ? "成年" : "未成年"; // 显式标注字符串类型
  console.log(`年龄:${age},状态:${adultStatus}`);

  // 场景2:判断商品是否库存充足
  const stock: number = 5; // 显式标注数字类型
  const stockStatus: string = stock > 0 ? "库存充足" : "已售罄"; // 显式标注字符串类型
  console.log(`商品库存:${stock},状态:${stockStatus}`);

  // 场景3:结合函数调用
  const score: number = 75; // 显式标注数字类型
  // 显式标注字符串类型
  const result: string = score >= 60 ?  "恭喜,考试及格!" : "很遗憾,考试不及格,请再接再厉!";
  console.log(`分数:${score},结果:${result}`);
}

运行效果

========== 基础三元运算符(简单二选一) ==========
年龄:20,状态:成年
商品库存:5,状态:库存充足
分数:75,结果:恭喜,考试及格!

5.2 统一测试函数

/**
 * 统一调用所有测试函数
 */
export function allTest(): void {
  testIfElseBasic();          // 基础if-else
  testIfElseMulti();          // 多条件if-else if-else
  testIfElseNested();         // 嵌套if-else
  testIfElseNullCheck();      // 布尔值隐式转换
  testSwitchBasic();          // 基础switch
  testSwitchFallthrough();    // switch穿透效应
  testSwitchWithEnum_Order(OrderStatus.Pending); // switch+枚举(订单)
  testSwitchWithEnum_Month(MonthEnum.Feb); // switch+枚举(月份)
  testTernaryBasic();         // 三元运算符
}

5.3 三元运算符的限制与注意事项

  1. 仅支持二选一:不能处理多条件逻辑(比如成绩等级判断),否则代码会变得冗长且可读性差;
  2. 避免嵌套使用:嵌套三元运算符(比如a ? b : c ? d : e)会让代码难以理解,建议用if-else替代;
  3. 复杂逻辑慎用:当返回值或条件表达式复杂时(比如多行代码),优先用if-else,保证可读性。

错误示例:嵌套三元运算符(不推荐)

// 不推荐:嵌套三元运算符,可读性差
const score: number = 85; // 显式标注数字类型
const level: string = score >= 90 ? "A" : score >= 80 ? "B" : score >= 60 ? "C" : "D"; // 显式标注字符串类型
console.log(`等级:${level}`); // 输出:B,但代码难以快速理解

六、分支语句适用场景与特性对比

语句类型 核心适用场景 核心优势 核心限制 典型业务场景
if-else 复杂条件、范围判断、嵌套逻辑 灵活适配任意布尔条件判断 多条件场景代码易冗长 成绩等级判断、登录验证、权限控制
switch 多等值条件、状态类判断 语法简洁、可读性强,适配枚举 仅支持等值判断,需手动加break 订单状态处理、月份季节判断、枚举匹配
三元运算符 简单二选一逻辑 一行代码完成,极简高效 不支持多条件,嵌套可读性极差 成年判断、库存状态、简单结果返回

七、核心总结

ArkTS分支语句的核心价值是“按条件精准执行差异化逻辑”,掌握其使用的关键在于精准选型+规范落地,而非单纯记忆语法:

1. 选型逻辑:贴合场景而非堆砌语法

基于条件特征选择对应语句,核心原则是“简单场景用简洁语法,复杂场景用灵活语法”:

  • 二选一的极简逻辑(如“是否成年”“库存是否充足”):优先用三元运算符,减少代码冗余;
  • 多值等值判断(如“订单状态”“月份匹配”):优先用switch(结合枚举可规避魔法字符串/数字,提升可维护性);
  • 范围/嵌套/复杂条件(如“成绩等级分层”“登录多维度验证”):用if-else,兼顾灵活性与逻辑完整性。

2. 规范落地:规避高频错误

  • 类型安全:条件表达式必须为布尔值,变量显式标注类型(number/string/enum等),杜绝隐式类型转换错误;
  • 语法约束:switch需为非穿透case添加break、保留default分支处理异常;if-else嵌套层级控制在3层内,避免逻辑混乱;
  • 边界覆盖:条件判断需考虑临界值(如60分及格线),枚举需定义在全局作用域,避免调用时“未定义”错误。

3. 核心原则:可读性>简洁性

无论选择哪种分支语句,最终都以“逻辑清晰、异常覆盖、易于维护”为核心——比如避免嵌套三元运算符(即便代码更短,也会大幅降低可读性),switch遗漏break(易触发非预期的穿透效应),这些看似“小问题”,却是编写ArkTS代码时最易踩的坑,也是影响代码可维护性的关键。

八、开始测试

// pages/Index.ets

import { allTest } from '../utils/FlowControlTest';

@Entry
@Component
struct Index {
  aboutToAppear(): void {
    allTest();
  }

  build() {
    Column() {
      Text("流程控制(上)——分支语句")
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Text("运行日志请查看控制台")
        .fontSize(16)
        .margin(20)
        .fontColor(Color.Grey)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }
}

九、代码仓库

十、下节预告

下一节我们将进入流程控制(下)——循环语句的学习,这是流程控制的另一核心内容,也是处理批量数据、重复逻辑的关键,重点包括:

  1. 掌握for循环的核心语法与场景(固定次数循环,如数组遍历、数字遍历),理解循环计数器的执行逻辑
  2. 掌握for...of循环的简洁遍历方式,学会处理数组、字符串等可迭代对象(含索引获取技巧)
  3. 掌握whiledo-while循环的用法,区分“先判断后执行”与“先执行后判断”的差异,学会预防死循环
  4. 学会使用breakcontinue关键字控制循环流程,优化循环执行效率(如提前终止、过滤无效迭代)
posted @ 2026-01-16 22:32  鸿蒙-散修  阅读(0)  评论(0)    收藏  举报