Day 04 - 循环与迭代
目标:掌握ArkTS的各种循环语句和数组迭代方法
预计时间:2-3小时
第一部分:for 循环
1.1 基本 for 循环
语法与C++相同:for (初始化; 条件; 更新) { 循环体 }
// IoT场景:轮询5个传感器读数
for (let i: number = 0; i < 5; i++) {
console.log(`传感器[${i}] 读数完成`);
}
// 输出:传感器[0] 读数完成 ... 传感器[4] 读数完成
// IoT场景:遍历设备列表
let devices: string[] = ["温度计-001", "湿度计-001", "烟雾-001", "灯-001"];
for (let i: number = 0; i < devices.length; i++) {
console.log(`设备[${i}]: ${devices[i]}`);
}
// 输出:
// 设备[0]: 温度计-001
// 设备[1]: 湿度计-001
// 设备[2]: 烟雾-001
// 设备[3]: 灯-001
1.2 for 循环变体
倒序遍历
// IoT场景:倒计时关闭设备
for (let i: number = 5; i > 0; i--) {
console.log(`设备将在 ${i} 秒后关闭`);
}
console.log("设备已关闭");
自定义步长
// IoT场景:每隔2个通道采样一次(共10个通道)
for (let channel: number = 0; channel < 10; channel += 2) {
console.log(`采样通道 ${channel}`);
}
// 输出:采样通道 0, 2, 4, 6, 8
// IoT场景:以10%为步长调整亮度
for (let brightness: number = 0; brightness <= 100; brightness += 10) {
console.log(`当前亮度:${brightness}%`);
}
多循环变量
⚠️ ArkTS支持:逗号运算符仅限 for 循环的初始化和更新表达式。
// ✅ 合法:for循环中使用逗号运算符
for (let i: number = 0, j: number = 10; i < j; i++, j--) {
console.log(`i=${i}, j=${j}`);
}
// 输出:
// i=0, j=10
// i=1, j=9
// i=2, j=8
// i=3, j=7
// i=4, j=6
省略部分表达式
// 省略初始化和更新(等同于while)
let retryCount: number = 0;
for (; retryCount < 3;) {
console.log(`第 ${retryCount + 1} 次重连...`);
retryCount++;
}
1.3 嵌套 for 循环
// IoT场景:扫描 3栋楼 × 4个房间 的设备状态
for (let building: number = 1; building <= 3; building++) {
for (let room: number = 1; room <= 4; room++) {
console.log(`检查楼栋${building}-房间${room} 的设备状态`);
}
}
// IoT场景:二维传感器矩阵读数(3行×3列)
let sensorMatrix: number[][] = [
[25, 26, 24],
[27, 25, 26],
[24, 25, 27]
];
for (let row: number = 0; row < sensorMatrix.length; row++) {
for (let col: number = 0; col < sensorMatrix[row].length; col++) {
console.log(`传感器[${row}][${col}] 温度: ${sensorMatrix[row][col]}°C`);
}
}
注意:嵌套层数越多,复杂度越高。实际开发中超过3层嵌套应考虑重构。
【第一部分小结】
| 变体 | 语法要点 | 典型场景 |
|---|---|---|
| 基本 for | for (let i=0; i<n; i++) |
已知次数的遍历 |
| 倒序 | i--,条件 i > 0 |
倒计时、反向遍历 |
| 自定义步长 | i += step |
间隔采样、分页 |
| 多变量 | let i=0, j=n; ...; i++, j-- |
双指针、对称操作 |
| 嵌套 | 外层变量对内层可见 | 矩阵、多维结构遍历 |
第二部分:while 和 do-while
2.1 while 循环
语法与C++相同:先检查条件,再执行循环体。
// IoT场景:等待设备上线(未知重试次数)
let isConnected: boolean = false;
let attempts: number = 0;
while (!isConnected && attempts < 10) {
console.log(`第 ${attempts + 1} 次尝试连接设备...`);
attempts++;
// 实际代码中这里会尝试真实连接
if (attempts === 3) {
isConnected = true; // 模拟第3次连接成功
}
}
if (isConnected) {
console.log(`连接成功,共尝试 ${attempts} 次`);
} else {
console.log("连接失败,超过最大重试次数");
}
// IoT场景:持续读取传感器,直到读数稳定
let lastReading: number = -1;
let currentReading: number = 0;
let stableCount: number = 0;
while (stableCount < 3) {
currentReading = 25; // 模拟读取传感器
if (currentReading === lastReading) {
stableCount++;
} else {
stableCount = 0;
}
lastReading = currentReading;
console.log(`读数: ${currentReading}°C,稳定次数: ${stableCount}`);
}
console.log(`传感器读数已稳定: ${currentReading}°C`);
2.2 do-while 循环(至少执行一次)
与C++相同:先执行循环体,再检查条件。至少执行一次。
// IoT场景:设备初始化(至少执行一次自检)
let initSuccess: boolean = false;
let initAttempts: number = 0;
do {
initAttempts++;
console.log(`第 ${initAttempts} 次设备自检...`);
// 模拟自检逻辑
if (initAttempts >= 2) {
initSuccess = true;
}
} while (!initSuccess && initAttempts < 5);
console.log(`设备自检完成,共自检 ${initAttempts} 次`);
// while vs do-while 的区别:
let condition: boolean = false;
// while:条件为false,一次都不执行
while (condition) {
console.log("while:这行不会执行");
}
// do-while:条件为false,仍执行一次
do {
console.log("do-while:这行会执行一次");
} while (condition);
2.3 while vs for 选择指南
// ✅ 已知循环次数 → 用 for
for (let i: number = 0; i < devices.length; i++) {
console.log(`处理设备 ${i}`);
}
// ✅ 未知次数,等待条件 → 用 while
let deviceReady: boolean = false;
while (!deviceReady) {
console.log("等待设备就绪...");
// deviceReady = checkDevice();
}
// ✅ 至少执行一次 → 用 do-while
do {
console.log("发送心跳包");
// sendHeartbeat();
} while (isAlive());
| 场景 | 推荐 | 原因 |
|---|---|---|
| 已知循环次数 | for |
结构紧凑,循环变量作用域清晰 |
| 未知次数,先判断 | while |
条件驱动,可能一次不执行 |
| 至少执行一次 | do-while |
保证循环体至少运行一次 |
| 等待某个外部状态 | while |
条件更灵活 |
【第二部分小结】
| 循环类型 | 条件检查时机 | 最少执行次数 | 典型用途 |
|---|---|---|---|
while |
执行前 | 0次 | 等待连接、轮询状态 |
do-while |
执行后 | 1次 | 设备初始化、菜单交互 |
for |
执行前 | 0次 | 已知次数遍历 |
第三部分:for-of 循环(推荐遍历数组)
3.1 基本用法
for-of 直接遍历数组元素值,无需手动管理索引,是遍历数组的推荐方式。
// IoT场景:遍历所有设备执行状态检查
let deviceNames: string[] = ["温度计-001", "湿度计-001", "烟雾-001", "灯-001"];
for (let device of deviceNames) {
console.log(`检查设备: ${device}`);
}
// 输出:
// 检查设备: 温度计-001
// 检查设备: 湿度计-001
// 检查设备: 烟雾-001
// 检查设备: 灯-001
// IoT场景:字符串数组遍历
let deviceNames: string[] = ["温度计-001", "湿度计-001", "烟雾-001", "灯-001"];
for (let name of deviceNames) {
console.log(`检查设备: ${name}`);
}
与普通 for 循环对比:
// 两种写法效果相同,for-of 更简洁
let readings: number[] = [23.5, 24.1, 22.8, 25.0];
// ✅ for-of:更简洁(不需要索引时推荐)
for (let reading of readings) {
console.log(`读数: ${reading}°C`);
}
// 等效的 for 写法
for (let i: number = 0; i < readings.length; i++) {
console.log(`读数: ${readings[i]}°C`);
}
3.2 需要索引时的写法
⚠️ ArkTS限制:不支持解构赋值,因此 for (let [index, device] of devices.entries()) 不合法。
// ❌ 错误:ArkTS不支持解构赋值
// for (let [index, device] of devices.entries()) {
// console.log(`${index}: ${device}`);
// }
// ✅ 方案一:改用普通 for 循环(最直接)
let deviceNames: string[] = ["温度计-001", "湿度计-001", "烟雾-001"];
for (let i: number = 0; i < deviceNames.length; i++) {
console.log(`[${i}] ${deviceNames[i]}`);
}
// ✅ 方案二:for-of + 手动计数器
let index: number = 0;
for (let device of deviceNames) {
console.log(`[${index}] ${device}`);
index++;
}
结论:需要索引时,优先用普通 for 循环,不要尝试 entries() + 解构。
3.3 遍历字符串
for-of 也可以遍历字符串,逐个访问每个字符。
// IoT场景:解析设备ID格式(如 "D001")
let deviceId: string = "D001";
for (let char of deviceId) {
console.log(`字符: ${char}`);
}
// 输出:
// 字符: D
// 字符: 0
// 字符: 0
// 字符: 1
// IoT场景:检查命令字符串是否只含数字和字母
let command: string = "CMD123";
let hasSpecialChar: boolean = false;
for (let char of command) {
let code: number = char.charCodeAt(0);
let isAlphaNum: boolean = (code >= 48 && code <= 57) || // 0-9
(code >= 65 && code <= 90) || // A-Z
(code >= 97 && code <= 122); // a-z
if (!isAlphaNum) {
hasSpecialChar = true;
break;
}
}
console.log(`命令 "${command}" 含特殊字符: ${String(hasSpecialChar)}`);
【第三部分小结】
| 场景 | 推荐写法 | 备注 |
|---|---|---|
| 只需元素值 | for (let item of arr) |
最简洁 |
| 需要索引 | 普通 for 循环 |
ArkTS无法用解构 |
| 遍历字符串字符 | for (let char of str) |
逐字符访问 |
第四部分:break 和 continue
4.1 break(终止循环)
break 立即终止当前循环,跳到循环后的第一条语句。
// IoT场景:发现第一个超温传感器后立即告警并停止扫描
let temperatures: number[] = [25, 92, 28, 26];
let overTempIndex: number = -1;
for (let i: number = 0; i < temperatures.length; i++) {
console.log(`扫描设备[${i}], 温度: ${temperatures[i]}°C`);
if (temperatures[i] > 90) {
overTempIndex = i;
break; // 找到超温设备,立即停止扫描
}
}
if (overTempIndex !== -1) {
console.log(`警告:设备[${overTempIndex}] 温度超限!`);
}
嵌套循环中的 break:只退出当前层循环。
// IoT场景:扫描楼层-房间矩阵,找到第一个故障设备后停止扫描当前楼层
for (let floor: number = 1; floor <= 3; floor++) {
console.log(`--- 扫描第${floor}层 ---`);
let floorDeviceIds: number[] = [101, 102, 103];
for (let i: number = 0; i < floorDeviceIds.length; i++) {
let deviceId: number = floorDeviceIds[i];
if (floor === 2 && deviceId === 102) {
console.log(`第${floor}层设备${deviceId}故障,停止扫描本层`);
break; // 只退出内层循环,外层继续
}
console.log(` 设备 ${deviceId} 正常`);
}
}
4.2 continue(跳过当前迭代)
continue 跳过本次迭代的剩余语句,直接进入下一次迭代。
// IoT场景:批量发送指令,跳过离线设备
let statuses: string[] = ["online", "offline", "online", "error", "online"];
let successCount: number = 0;
for (let i: number = 0; i < statuses.length; i++) {
if (statuses[i] !== "online") {
console.log(`跳过设备[${i}](状态: ${statuses[i]})`);
continue; // 跳过非在线设备
}
// 只有 online 的设备才会执行到这里
console.log(`向设备[${i}] 发送指令`);
successCount++;
}
console.log(`成功发送指令给 ${successCount} 个设备`);
continue 与 if 嵌套的等效写法对比:
// ✅ 使用 continue(扁平化代码,更清晰)
for (let status of statuses) {
if (status !== "online") continue;
console.log(`处理在线设备`);
}
// 等效但嵌套更深的写法
for (let status of statuses) {
if (status === "online") {
console.log(`处理在线设备`);
}
}
4.3 带标签的 break(跳出多层循环)
当需要从嵌套循环内部直接跳出外层循环时,使用带标签的 break。
// IoT场景:在楼层-房间-设备三层结构中,发现严重故障立即终止所有扫描
let found: boolean = false;
let foundFloor: number = 0;
let foundRoom: number = 0;
outerScan: for (let floor: number = 1; floor <= 3; floor++) {
for (let room: number = 1; room <= 4; room++) {
console.log(`扫描 ${floor}楼-${room}号房`);
// 模拟:2楼3号房发现严重故障
if (floor === 2 && room === 3) {
found = true;
foundFloor = floor;
foundRoom = room;
break outerScan; // 直接跳出外层 outerScan 循环
}
}
}
if (found) {
console.log(`严重故障:${foundFloor}楼-${foundRoom}号房,已终止全部扫描`);
}
// 对比:不带标签的break(只退出内层)
for (let floor: number = 1; floor <= 3; floor++) {
for (let room: number = 1; room <= 4; room++) {
if (floor === 2 && room === 3) {
break; // 只退出 room 循环,floor 循环继续
}
}
console.log(`${floor}楼扫描完成`); // 仍会执行
}
⚠️ 提示:带标签的 break 对应 C++ 的 goto,但语义更明确、更安全。
标签使用规则
标签只能标记以下语句:for、while、do-while、switch
// ✅ 正确:标签在 for 循环前面
outer: for (let i: number = 0; i < 3; i++) {
break outer;
}
// ❌ 错误:标签后不是循环语句
label: {
// 普通代码块,不能用 break label
}
break 只能跳出包含它的、被标签标记的语句:
- 不能跳入代码块
- 不能跳出函数
- 不能跳过变量初始化
// ❌ 错误:不能跳出函数
function test(): void {
outer: for (let i: number = 0; i < 3; i++) {
if (i === 1) {
break outer; // 这是可以的,在函数内部跳循环
}
}
}
// ❌ 错误:不能跳入另一个代码块
if (condition) {
target: console.log("目标"); // 标签不能标记语句
}
【第四部分小结】
| 语句 | 作用范围 | 效果 |
|---|---|---|
break |
当前循环层 | 立即终止当前循环 |
continue |
当前循环层 | 跳过本次迭代,继续下一次 |
break label |
指定标签的循环层 | 跳出到标签所在循环之外 |
第五部分:ArkTS 循环限制(重要提醒)
6.1 不支持 for...in(官方明确禁止)
ArkTS 官方规范明确禁止 for...in 语法。
// ❌ 错误:ArkTS不支持 for...in
// for (let key in someObject) {
// console.log(key);
// }
// ✅ 替代方案一:直接访问已知属性
let deviceId: number = 1001;
let deviceName: string = "灯-001";
console.log(`id: ${deviceId}`);
console.log(`name: ${deviceName}`);
// ✅ 替代方案二:对数组用 for 循环(如果原本用 for...in 遍历数组索引)
let deviceNames: string[] = ["灯-001", "灯-002", "灯-003"];
// ❌ 用 for...in 遍历数组(ArkTS不支持,且有潜在风险)
// for (let index in deviceNames) {
// console.log(deviceNames[index]);
// }
// ✅ 正确:用 for 循环或 for-of
for (let i: number = 0; i < deviceNames.length; i++) {
console.log(`[${i}] ${deviceNames[i]}`);
}
for (let name of deviceNames) {
console.log(name);
}
6.2 不支持解构赋值(entries() 不能解构)
ArkTS 明确不支持解构变量声明(数组解构和对象解构均不支持)。
let devices: string[] = ["灯-001", "灯-002", "灯-003"];
// ❌ 错误:ArkTS不支持数组解构
// for (let [index, device] of devices.entries()) {
// console.log(`${index}: ${device}`);
// }
// ❌ 错误:ArkTS不支持对象解构
// let { id, name } = device;
// ✅ 替代方案一:用普通 for 循环(同时获得索引和值)
for (let i: number = 0; i < devices.length; i++) {
console.log(`[${i}] ${devices[i]}`);
}
// ✅ 替代方案二:for-of + 手动计数器
let idx: number = 0;
for (let device of devices) {
console.log(`[${idx}] ${device}`);
idx++;
}
// ✅ 替代方案三:对象属性用点运算符访问
// 假设有一个设备对象
let deviceObj: object = { id: 1001, name: "灯-001" };
// 直接通过属性名访问,不能用解构
let devId: number = deviceObj.id;
let devName: string = deviceObj.name;
console.log(`设备: ${devId} - ${devName}`);
【第六部分小结】
| 限制 | 错误写法 | 替代方案 |
|---|---|---|
不支持 for...in |
for (let k in obj) |
直接访问属性 / 用 for/for-of |
| 不支持数组解构 | let [a, b] = arr |
用 arr[0], arr[1] |
| 不支持对象解构 | let {id, name} = obj |
用 obj.id, obj.name |
| 不支持解构迭代 | for (let [i, v] of arr.entries()) |
普通 for 循环 |
测试题
题1:判断输出(基础)
以下代码的输出结果是什么?
let total: number = 0;
for (let i: number = 1; i <= 5; i++) {
if (i % 2 === 0) continue;
total += i;
}
console.log(`total = ${total}`);
答案
total = 9
分析:只累加奇数:1 + 3 + 5 = 9。偶数(2、4)被 continue 跳过。
题2:判断输出(标签break)
以下代码的输出结果是什么?
outer: for (let i: number = 0; i < 3; i++) {
for (let j: number = 0; j < 3; j++) {
if (i + j >= 3) {
break outer;
}
console.log(`i=${i}, j=${j}`);
}
}
答案
i=0, j=0
i=0, j=1
i=0, j=2
i=1, j=0
i=1, j=1
分析:当 i=1, j=2 时,1+2=3 >= 3,执行 break outer,跳出外层循环。在此之前 i=0,j=0/1/2 和 i=1,j=0/1 均被打印。
题3:找错题(ArkTS限制)
下面代码有几处 ArkTS 编译错误?指出并修正。
let values: number[] = [25, 92, 28];
// 错误1
for (let key in values) {
console.log(key);
}
// 错误2
for (let [index, value] of values.entries()) {
console.log(String(value));
}
答案
错误1:ArkTS 不支持 for...in。
修正:改用 for (let i: number = 0; i < values.length; i++)
错误2:ArkTS 不支持解构赋值 [index, value]。
修正:改用普通 for 循环并通过索引访问:
for (let i: number = 0; i < values.length; i++) {
console.log(String(values[i]));
}
题4:编程题(设备轮询)
需求:实现一个设备状态轮询函数。
给定设备数组(每个设备包含 status 和 powerWatts 属性),使用循环:
- 跳过状态为
"error"的设备 - 统计在线设备(
status === "online")的总功耗 - 发现功耗超过 2000W 时立即停止轮询并告警
- 返回总功耗、轮询设备数、是否告警
function pollDevices(devices: object[]): object {
// 请在此实现
}
参考答案
function pollDevices(devices: object[]): object {
let totalPower: number = 0;
let polledCount: number = 0;
let alarm: boolean = false;
for (let device of devices) {
if (device.status === "error") {
continue; // 跳过故障设备
}
polledCount++;
if (device.status === "online") {
totalPower += device.powerWatts;
}
if (totalPower > 2000) {
alarm = true;
break; // 超过阈值立即停止
}
}
return { totalPower: totalPower, polledCount: polledCount, alarm: alarm };
}
循环语句速查表
| 语句 | 语法 | ArkTS支持 | 主要用途 |
|---|---|---|---|
for |
for (let i=0; i<n; i++) |
✅ | 已知次数遍历、需要索引 |
while |
while (cond) { } |
✅ | 未知次数、等待条件 |
do-while |
do { } while (cond) |
✅ | 至少执行一次 |
for-of |
for (let x of arr) |
✅ | 遍历数组元素(推荐) |
for-in |
for (let k in obj) |
❌ | ArkTS禁止 |
| 解构迭代 | for (let [i,v] of arr.entries()) |
❌ | ArkTS禁止 |
break |
break |
✅ | 终止当前循环 |
break label |
break label |
✅ | 跳出多层循环 |
continue |
continue |
✅ | 跳过当前迭代 |

浙公网安备 33010602011771号