全国协议5人面授小班,企业级独立开发考核,转业者的IT软件工程师基地 登录/注册 | 如何报名
当前位置: 前端开发   >  TypeScript 迭代器(Iterator)
admin · 更新于 2021-08-06

1. 解释

迭代器是一种特殊对象,它符合迭代器协议规范。在 TypeScript 中,我们可以定义一个接口,这个接口上有一个函数类型 next ,next() 方法的返回值类型是 { value: any, done: boolean }。其中,value 是 any 类型,表示下一个将要返回的值;done 是布尔类型,当没有更多可返回数据时返回 true。迭代器还会保存一个内部指针,用来指向当前集合中值的位置。

迭代器一旦创建,迭代器对象就可以通过重复调用 next() 显式地迭代。

2. 模拟一个简略的迭代器

实例演示
interface IteratorInterface {
  next: () => {
    value: any
    done: boolean
  }}function createIterator(array: any[]): IteratorInterface {
  let index = 0
  let len = array.length  return {
    next: function () {
      return index < len ? { value: array[index++], done: false } : { value: undefined, done: true }
    }
  }}var iterator = createIterator([1, 2, 3])console.log(iterator.next()) // { value: 1, done: false }console.log(iterator.next()) // { value: 2, done: false }console.log(iterator.next()) // { value: 3, done: false }console.log(iterator.next()) // { value: undefined, done: true }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
运行案例点击 "运行案例" 可查看在线运行效果

代码解释:

第 1 行,声明了一个 Iterator 接口,具有 next 这样一个函数类型。

第 8 行,声明了一个可以返回迭代器对象的函数,这个函数的返回值类型必须符合 Iterator 接口。

倒数第 4 行,通过调用迭代器对象上的 next() 方法,可以拿到数据集中的下一个数据项。

最后一行,拿到数据集中的所有数据后,done 属性变为 true。

3. 可迭代性

上面的例子,用模拟的迭代器地迭代了一个数组对象,那是不是所有的对象都可以这样迭代呢?当然不是。

只有一个对象实现了 Symbol.iterator 属性时,我们才认为它是可迭代的。一些内置的类型如 Array,Map,Set,String,Int32Array,Uint32Array 等都已经实现了各自的 Symbol.iterator

Symbol.iterator 属性本身是一个函数,就是当前数据结构默认的迭代器生成函数。执行这个函数,就会返回一个迭代器。

比如,String 是一个内置的可迭代对象:

let str: string = 'Hi'console.log(typeof str[Symbol.iterator]) // function
代码块
  • 1
  • 2

String 的默认迭代器会依次返回该字符串的字符:

实例演示
let str: string = 'Hi'let iterator: IterableIterator<string> = str[Symbol.iterator]()
 console.log(iterator.next())      // { value: 'H', done: false }console.log(iterator.next())      // { value: 'i', done: false }console.log(iterator.next())      // { value: undefined, done: true }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
运行案例点击 "运行案例" 可查看在线运行效果

代码解释:

第 1 行,声明一个字符串类型变量,字符串类型内置了默认迭代器生成函数 Symbol.iterator

第 2 行,执行这个函数,返回了一个迭代器。

总结一下迭代器的作用:

  • 为各种数据结构(Array,Map,Set,String等),提供一个统一的、简便的访问接口。
  • 使得数据结构的成员能够按某种次序排列。
  • 创造了一种新的遍历命令 for..of 循环。

4. for…of

for...of 会遍历可迭代的对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等),调用对象上的 Symbol.iterator 方法。

4.1 迭代数组

实例演示
let iterable = [10, 20, 30]for (const value of iterable) {
  console.log(value)}// 10// 20// 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
运行案例点击 "运行案例" 可查看在线运行效果

解释: 通过 for...of 循环遍历数组 iterable 的每一项元素。

实例演示
const heroes = [
  {
    name: '艾希',
    gender: 2
  },
  {
    name: '泰达米尔',
    gender: 1
  }]for (let { name } of heroes) {
  console.log(name)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
运行案例点击 "运行案例" 可查看在线运行效果

解释: 通过 let { name } of heroes 循环迭代 heroes 对象数组,将每一个对象解构,得到每一个对象的 name 属性值。

4.2 迭代字符串

实例演示
let iterable = 'hanma'for (const s of iterable) {
  console.log(s)}// i// m// o// o// c
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
运行案例点击 "运行案例" 可查看在线运行效果

字符串具有可迭代性,通过 for...of 可以快速遍历出每一个字符。

4.3 迭代 Map:

实例演示
let iterable = new Map()iterable.set('a', 1)iterable.set('b', 2)iterable.set('c', 3)for (let entry of iterable) {
  console.log(entry)}// ['a', 1]// ['b', 2]// ['c', 3]for (let [key, value] of iterable) {
  console.log(value)}// 1// 2// 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
运行案例点击 "运行案例" 可查看在线运行效果

解释: 一个 Map 对象在迭代时会根据对象中元素的插入顺序来进行。for...of 循环在每次迭代后会返回一个形式为 [key,value] 的数组。通过使用 let [key, value] 这种解构形式,可以快速获取每一项属性值。

5. for…of 与 for…in 的区别

  • for...of 语句遍历可迭代对象定义要迭代的数据。
  • for...in 语句以任意顺序迭代对象的可枚举属性
实例演示
let iterable: number[] = [3, 5, 7]for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i)
  }}// 0// 1// 2for (let i of iterable) {
  console.log(i)}// 3// 5// 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
运行案例点击 "运行案例" 可查看在线运行效果

for...in 可以操作任何对象,迭代对象的可枚举属性。但是 for...of 只关注于可迭代对象的值。

6. 解构赋值与扩展运算符

对数组和 Set 结构进行解构赋值时,会默认调用 Symbol.iterator 方法:

let [head, ...tail] = [1, 2, 3, 4]// tail = [2, 3, 4]
代码块
  • 1
  • 2

扩展运算符也会调用默认的 Iterator 接口,得到一个数组结构:

let arr = [...'hanma']console.log(arr) //  ['i','m','o','o','c']
代码块
  • 1
  • 2

7. 小结

本小节介绍了迭代器的一些具体使用,要注意 for...of 与 for...in 的区别。

另外,可以借助编辑器(如 vscode)查看一下 TypeScript 迭代器接口定义的源码:

interface IteratorYieldResult<TYield> {
  done?: false
  value: TYield}interface IteratorReturnResult<TReturn> {
  done: true
  value: TReturn}type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>interface Iterator<T, TReturn = any, TNext = undefined> {
  // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
  next(...args: [] | [TNext]): IteratorResult<T, TReturn>
  return?(value?: TReturn): IteratorResult<T, TReturn>
  throw?(e?: any): IteratorResult<T, TReturn>}interface IterableIterator<T> extends Iterator<T> {
    [Symbol.iterator](): IterableIterator<T>;}
代码块
预览复制
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22



为什么选择汉码未来