TypeScript 迭代器和生成器详解
目录
迭代器(Iterators)
生成器(Generators)
使用场景
for..of vs. for..in 语句
for..of 循环
for..in 循环
区别总结
注意事项
总结
在 TypeScript 中,迭代器(Iterators)和生成器(Generators)是处理数据集合和异步编程的重要工具。它们能够帮助我们更优雅地处理数据流和异步操作,
迭代器(Iterators)
迭代器是一种设计模式,它提供一种方法来顺序访问一个聚合对象中的元素,而不需要暴露对象的内部表示。在 TypeScript 中,迭代器允许我们通过一种统一的方式来访问不同类型的数据集合,比如数组、集合、映射等。
Iterable 和 Iterator
在理解迭代器之前,需要了解两个核心概念:Iterable 和 Iterator。
- Iterable(可迭代对象):实现了 Symbol.iterator 方法的对象称为可迭代对象。这个方法返回一个迭代器。
- Iterator(迭代器):是一个对象,它实现了 next() 方法,每次调用 next() 方法会返回一个包含 value 和 done 属性的对象。value 表示当前迭代到的值,done 表示迭代器是否已经完成迭代。
示例
let numbers = [1, 2, 3, 4, 5];// 获取迭代器
let iterator = numbers[Symbol.iterator]();// 1、使用迭代器遍历数组
let next = iterator.next();
while (!next.done) {console.log(next.value);next = iterator.next();
}
// 2、使用for..of遍历数组
// for (let value of iterator) {
// console.log(value);
// }
在这个例子中,numbers 是一个数组,我们通过 numbers[Symbol.iterator]() 获取了一个迭代器,然后使用 iterator.next() 方法来依次访问数组的元素。
生成器(Generators)
生成器是一种特殊类型的函数,它可以暂停执行,并且可以在需要时恢复执行。生成器函数使用 function* 关键字来定义,而不是普通函数的 function 关键字。
语法
生成器函数使用 function* 声明,内部可以使用 yield 关键字来暂停函数执行,并将一个值传递给生成器的调用者。
function* generateSequence() {yield 1;yield 2;yield 3;
}// 使用生成器
let generator = generateSequence();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }
在这个例子中,generateSequence 是一个生成器函数,通过 yield 关键字定义了一系列的值。每次调用 generator.next() 会恢复生成器函数执行,直到遇到下一个 yield 或者函数结束为止。
使用场景
迭代器的应用场景:
- 遍历集合(如数组、映射等)中的元素。
- 自定义迭代器,以便在数据结构中实现自定义的迭代行为。
生成器的应用场景:
- 异步编程:生成器函数可以与 async/await 结合使用,使异步代码看起来更像同步代码,提升代码的可读性和维护性。
- 惰性计算:生成器可以用于生成无限序列或者大数据集,只在需要时生成下一个值,节省内存和计算资源。
for..of vs. for..in 语句
for..of 循环
用法:
- 用于迭代可迭代对象(例如数组、字符串、Set、Map 等)的值。
- for..of 语句会遍历对象的元素,而不是对象本身的属性。
示例:
let iterable = [10, 20, 30];for (let value of iterable) {console.log(value); // 输出 10, 20, 30
}
在这个例子中,for..of 循环直接迭代数组 iterable 中的每一个值。
for..in 循环
用法:
- 用于迭代对象的可枚举属性,包括从原型链继承的属性。
- for..in 循环通常用于遍历对象的键名。
示例:
let obj = { a: 1, b: 2, c: 3 };for (let key in obj) {console.log(key); // 输出 'a', 'b', 'c'
}
在这个例子中,for..in 循环遍历对象 obj 的每一个键名。
区别总结
for..of:
- 遍历值:遍历集合的值而不是属性。
- 适用于:数组、Set、Map、字符串等实现了可迭代协议的对象。
for..in:
- 遍历键名:遍历对象的键名,包括继承的属性。
- 适用于:普通对象的属性遍历,通常不用于数组或类似数组对象的迭代。
注意事项
- for..of 不适用于普通对象,因为普通对象并不是可迭代对象,如果尝试对普通对象使用 for..of 会导致语法错误或无法正常遍历。
- for..in 在遍历数组时,除了遍历数组自身的索引外,还会遍历原型链上的可枚举属性,因此通常不推荐用于遍历数组,除非确实需要包括原型链上的属性。
总结
for..of 用于遍历集合的值,而 for..in 用于遍历对象的属性(键名)。