Skip to content

TypeScript 循环结构

循环结构是编程中常用的控制结构,用于重复执行一段代码。TypeScript 支持与 JavaScript 相同的循环结构,包括 for 循环、for-in 循环、for-of 循环、while 循环和 do-while 循环。本文将详细介绍 TypeScript 中的循环结构。

1. for 循环

for 循环是最常用的循环结构,用于在指定条件下重复执行代码。

语法

typescript
for (初始化表达式; 条件表达式; 更新表达式) {
  // 循环体代码
}

示例

typescript
// 打印 1 到 5 的数字
for (let i = 1; i <= 5; i++) {
  console.log(i);
}

// 输出:
// 1
// 2
// 3
// 4
// 5

嵌套 for 循环

typescript
// 打印乘法表
for (let i = 1; i <= 9; i++) {
  for (let j = 1; j <= i; j++) {
    console.log(`${j} * ${i} = ${j * i}`);
  }
  console.log('---');
}

2. for-in 循环

for-in 循环用于遍历对象的可枚举属性。

语法

typescript
for (let 变量 in 对象) {
  // 循环体代码
}

示例

typescript
// 遍历对象的属性
const person = {
  name: "John",
  age: 30,
  city: "New York"
};

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

// 输出:
// name: John
// age: 30
// city: New York

// 遍历数组的索引
const fruits = ["apple", "banana", "cherry"];

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

// 输出:
// 0: apple
// 1: banana
// 2: cherry

注意事项

  1. for-in 循环会遍历对象的所有可枚举属性,包括继承的属性。
  2. 对于数组,for-in 循环遍历的是数组的索引,而不是元素值。
  3. 为了避免遍历到继承的属性,可以使用 hasOwnProperty 方法进行检查。
typescript
// 使用 hasOwnProperty 检查
const person = {
  name: "John",
  age: 30,
  city: "New York"
};

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

3. for-of 循环

for-of 循环用于遍历可迭代对象(如数组、字符串、Map、Set 等)的元素。

语法

typescript
for (let 变量 of 可迭代对象) {
  // 循环体代码
}

示例

typescript
// 遍历数组的元素
const fruits = ["apple", "banana", "cherry"];

for (let fruit of fruits) {
  console.log(fruit);
}

// 输出:
// apple
// banana
// cherry

// 遍历字符串的字符
const str = "Hello";

for (let char of str) {
  console.log(char);
}

// 输出:
// H
// e
// l
// l
// o

// 遍历 Map 的条目
const map = new Map();
map.set("name", "John");
map.set("age", 30);

for (let [key, value] of map) {
  console.log(`${key}: ${value}`);
}

// 输出:
// name: John
// age: 30

// 遍历 Set 的元素
const set = new Set([1, 2, 3, 4, 5]);

for (let item of set) {
  console.log(item);
}

// 输出:
// 1
// 2
// 3
// 4
// 5

注意事项

  1. for-of 循环只能用于可迭代对象。
  2. 对于数组,for-of 循环遍历的是数组的元素值,而不是索引。
  3. for-of 循环不能直接遍历对象,需要使用 Object.entries()Object.keys()Object.values() 方法。
typescript
// 遍历对象的键值对
const person = {
  name: "John",
  age: 30,
  city: "New York"
};

// 使用 Object.entries()
for (let [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

// 使用 Object.keys()
for (let key of Object.keys(person)) {
  console.log(`${key}: ${person[key]}`);
}

// 使用 Object.values()
for (let value of Object.values(person)) {
  console.log(value);
}

4. while 循环

while 循环用于在条件为真时重复执行代码。

语法

typescript
while (条件) {
  // 循环体代码
}

示例

typescript
// 打印 1 到 5 的数字
let i = 1;
while (i <= 5) {
  console.log(i);
  i++;
}

// 输出:
// 1
// 2
// 3
// 4
// 5

注意事项

  1. 确保循环条件最终会变为 false,否则会导致无限循环。
  2. 循环体中必须包含更新循环变量的代码,否则会导致无限循环。
typescript
// 无限循环(危险)
// while (true) {
//   console.log("无限循环");
// }

5. do-while 循环

do-while 循环与 while 循环类似,但它会先执行一次循环体,然后再检查条件。

语法

typescript
do {
  // 循环体代码
} while (条件);

示例

typescript
// 打印 1 到 5 的数字
let i = 1;
do {
  console.log(i);
  i++;
} while (i <= 5);

// 输出:
// 1
// 2
// 3
// 4
// 5

// 即使条件为假,也会执行一次循环体
let j = 6;
do {
  console.log(j);
  j++;
} while (j <= 5);

// 输出:
// 6

6. 循环控制语句

break 语句

break 语句用于跳出循环,终止循环的执行。

typescript
// 使用 break 跳出循环
for (let i = 1; i <= 10; i++) {
  if (i === 5) {
    break;
  }
  console.log(i);
}

// 输出:
// 1
// 2
// 3
// 4

continue 语句

continue 语句用于跳过当前循环的剩余部分,进入下一次循环。

typescript
// 使用 continue 跳过偶数
for (let i = 1; i <= 10; i++) {
  if (i % 2 === 0) {
    continue;
  }
  console.log(i);
}

// 输出:
// 1
// 3
// 5
// 7
// 9

label 语句

label 语句用于标记循环,配合 breakcontinue 语句使用,可以跳出或继续指定的循环。

typescript
// 使用 label 跳出嵌套循环
outerLoop: for (let i = 1; i <= 3; i++) {
  for (let j = 1; j <= 3; j++) {
    if (i === 2 && j === 2) {
      break outerLoop;
    }
    console.log(`i: ${i}, j: ${j}`);
  }
}

// 输出:
// i: 1, j: 1
// i: 1, j: 2
// i: 1, j: 3
// i: 2, j: 1

7. 数组方法

TypeScript 中的数组提供了一些方法,可以替代传统的循环结构,使代码更简洁、更易读。

forEach 方法

forEach 方法用于遍历数组的每个元素,并对每个元素执行指定的函数。

typescript
const fruits = ["apple", "banana", "cherry"];

fruits.forEach((fruit, index) => {
  console.log(`${index}: ${fruit}`);
});

// 输出:
// 0: apple
// 1: banana
// 2: cherry

map 方法

map 方法用于遍历数组的每个元素,并返回一个新的数组,新数组的元素是原数组元素经过指定函数处理后的结果。

typescript
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map(num => num * 2);

console.log(doubledNumbers); // 输出:[2, 4, 6, 8, 10]

filter 方法

filter 方法用于遍历数组的每个元素,并返回一个新的数组,新数组的元素是原数组中满足指定条件的元素。

typescript
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);

console.log(evenNumbers); // 输出:[2, 4]

reduce 方法

reduce 方法用于遍历数组的每个元素,并将其累加到一个累加器中,返回最终的累加结果。

typescript
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, num) => acc + num, 0);

console.log(sum); // 输出:15

some 方法

some 方法用于检查数组中是否至少有一个元素满足指定条件。

typescript
const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0);

console.log(hasEven); // 输出:true

every 方法

every 方法用于检查数组中是否所有元素都满足指定条件。

typescript
const numbers = [1, 2, 3, 4, 5];
const allPositive = numbers.every(num => num > 0);

console.log(allPositive); // 输出:true

find 方法

find 方法用于查找数组中第一个满足指定条件的元素。

typescript
const numbers = [1, 2, 3, 4, 5];
const firstEven = numbers.find(num => num % 2 === 0);

console.log(firstEven); // 输出:2

findIndex 方法

findIndex 方法用于查找数组中第一个满足指定条件的元素的索引。

typescript
const numbers = [1, 2, 3, 4, 5];
const firstEvenIndex = numbers.findIndex(num => num % 2 === 0);

console.log(firstEvenIndex); // 输出:1

8. 最佳实践

1. 选择合适的循环结构

  • 对于已知循环次数的情况,使用 for 循环。
  • 对于遍历对象的属性,使用 for-in 循环。
  • 对于遍历可迭代对象的元素,使用 for-of 循环。
  • 对于条件循环,使用 whiledo-while 循环。
  • 对于数组操作,优先使用数组方法(如 forEachmapfilter 等)。

2. 避免无限循环

确保循环条件最终会变为 false,循环体中包含更新循环变量的代码。

typescript
// 错误(无限循环)
// let i = 1;
// while (i <= 5) {
//   console.log(i);
//   // 没有更新 i
// }

// 正确
let i = 1;
while (i <= 5) {
  console.log(i);
  i++;
}

3. 保持循环简洁

循环体应该简洁明了,避免过于复杂的逻辑。如果循环体过长,可以考虑将其提取为一个函数。

typescript
// 推荐
function processItem(item) {
  // 处理逻辑
  console.log(item);
}

const items = [1, 2, 3, 4, 5];
for (let item of items) {
  processItem(item);
}

// 不推荐
const items = [1, 2, 3, 4, 5];
for (let item of items) {
  // 复杂的处理逻辑
  console.log(item);
  // 更多代码...
}

4. 使用类型注解

在 TypeScript 中,为循环变量添加类型注解可以提高代码的可读性和类型安全性。

typescript
// 推荐
const numbers: number[] = [1, 2, 3, 4, 5];
for (let num: number of numbers) {
  console.log(num);
}

// 不推荐
const numbers = [1, 2, 3, 4, 5];
for (let num of numbers) {
  console.log(num);
}

5. 避免在循环中修改数组

在遍历数组时,避免修改数组的长度或内容,否则可能会导致意外的行为。

typescript
// 错误(在循环中修改数组)
const numbers = [1, 2, 3, 4, 5];
for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] === 3) {
    numbers.splice(i, 1); // 删除元素,会影响数组长度
  }
  console.log(numbers[i]);
}

// 正确(先创建新数组)
const numbers = [1, 2, 3, 4, 5];
const filteredNumbers = numbers.filter(num => num !== 3);
for (let num of filteredNumbers) {
  console.log(num);
}

9. 常见错误

1. 无限循环

忘记更新循环变量或循环条件永远为真,导致无限循环。

typescript
// 错误(无限循环)
// for (let i = 1; ; i++) {
//   console.log(i);
// }

// 错误(无限循环)
// let i = 1;
// while (true) {
//   console.log(i);
// }

2. 循环变量作用域问题

在使用 var 声明循环变量时,由于 var 是函数作用域,可能会导致循环变量共享的问题。

typescript
// 错误(循环变量共享)
for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // 输出:5, 5, 5, 5, 5
  }, 100);
}

// 正确(使用 let 声明循环变量)
for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // 输出:0, 1, 2, 3, 4
  }, 100);
}

3. 混淆 for-in 和 for-of

for-in 循环遍历的是对象的属性或数组的索引,而 for-of 循环遍历的是可迭代对象的元素。

typescript
const fruits = ["apple", "banana", "cherry"];

// 错误(使用 for-in 遍历数组元素)
for (let fruit in fruits) {
  console.log(fruit); // 输出:0, 1, 2(索引)
}

// 正确(使用 for-of 遍历数组元素)
for (let fruit of fruits) {
  console.log(fruit); // 输出:apple, banana, cherry(元素)
}

4. 数组方法的使用错误

在使用数组方法时,需要注意方法的返回值和参数。

typescript
const numbers = [1, 2, 3, 4, 5];

// 错误(forEach 方法没有返回值)
// const doubledNumbers = numbers.forEach(num => num * 2);
// console.log(doubledNumbers); // 输出:undefined

// 正确(使用 map 方法)
const doubledNumbers = numbers.map(num => num * 2);
console.log(doubledNumbers); // 输出:[2, 4, 6, 8, 10]

总结

TypeScript 支持多种循环结构,包括:

  • for 循环:用于在指定条件下重复执行代码
  • for-in 循环:用于遍历对象的可枚举属性
  • for-of 循环:用于遍历可迭代对象的元素
  • while 循环:用于在条件为真时重复执行代码
  • do-while 循环:先执行一次循环体,然后再检查条件

此外,TypeScript 还提供了多种数组方法,如 forEachmapfilterreduce 等,可以替代传统的循环结构,使代码更简洁、更易读。

通过合理使用这些循环结构和数组方法,你可以编写更清晰、更高效的 TypeScript 代码。