您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

TypeScript 迭代器(Iterator)

如果要从数据集中数据项,可以对这个数据集进行迭代。

JavaScript 提供了许多迭代集合的,从简单的 for 循环到 map()filter()。本节要介绍的迭代器也是一种方案,并且迭代器将迭代的概念直接带入核心语言,同时提供了一种机制来 for...of 循环的行为。

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

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

interface IteratorInterface {
  next: () => {
    value: any
    done: boolean
  }
}

function createIterator(array: any[]): IteratorInterface {
  let index = 
  let len = array.length

  return {
    next: function () {
      return index < len ? { value: array[index++], done: false } : { value: undefined, done: true }
    }
  }
}

var iterator = createIterator([, , ])

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 行,声明了 Iterator 接口,具有 next 这样类型。

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

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

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

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

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

Symbol.iterator 本身是,就是当前数据结构认的迭代器。执行这个,就会返回迭代器。

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

let str: string = 'Hi'
console.log(typeof str[Symbol.iterator]) // function

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 行,声明字符串类型变量,字符串类型内置了认迭代器 Symbol.iterator

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

总结一下迭代器的作用:

for...of 会遍历可迭代的对象( Array,Map,Set,String,TypedArray,arguments 对象等等),对象上的 Symbol.iterator

let iterable = [, , ]

for (const value of iterable) {
  console.log(value)
}
// 10
// 20
// 30

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

const heroes = [
  {
    name: '艾希',
    gender: 
  },
  {
    name: '泰达米尔',
    gender: 
  }
]

for (let { name } of heroes) {
  console.log(name)
}

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

let iterable = 'imooc'

for (const s of iterable) {
  console.log(s)
}
// i
// m
// o
// o
// c

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

let iterable = new Map()

iterable.set('a', )
iterable.set('b', )
iterable.set('c', )

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

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

let iterable: number[] = [, , ]

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i)
  }
}
// 0
// 1
// 2

for (let i of iterable) {
  console.log(i)
}
// 3
// 5
// 7

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

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

let [head, ...tail] = [, , , ]
// tail = [2, 3, 4]

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

let arr = [...'imooc']
console.log(arr) //  ['i','m','o','o','c']

本小节介绍了迭代器的一些具体使用,要注意 for...offor...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>;
}

联系我
置顶