Iterator和Generator

7/20/2022 js

# 1.Iterator

# 1.1 什么是Iterator

Iterator迭代器,它定义了对象的遍历机制。

它有一个next()方法,在遍历时会调用iterator.next(),会返回结果{value,done}

  • value为本轮迭代拿到的值
  • done为布尔值,标志是否遍历结束,从而控制遍历操作

定义对象的迭代器也很简单:为其定义[Symbol.iterator]方法从中返回一个含有next方法的迭代器对象即可

不多bb,来看代码:

// 定义obj的[Symbol.iterator]
let obj	 = {
  properties:[ ['id',107], ['age',18] ],
  
  [Symbol.iterator](){
    let curIndex = 0 // 迭代器指针,每轮迭代结束都会偏移,指示下一轮迭代索引
    let self = this
    return { // 返回一个Iterator对象,含有一个next方法
      next(){
        let done = curIndex > self.properties.length-1 // 是否遍历结束
        return { value:self.properties[curIndex++],done} // curIndex++控制迭代器指针偏移一位
      }
    }
  }
}

定义了Iterator后便可以通过for...of来进行遍历,其遍历过程伪代码如下:

for(let item of obj){
  // ...
}

// 等价于
if([Symbol.iterator] in obj){ // 定义了迭代器
 let iterator = obj[Symbol.iterator]() // 获取obj的迭代器
  for(let res = iterator.next(); !res.done; res=iterator.next()){
    item = res.value // 将value赋给item   
    // ...
  }
}else throw new Error('obj is not iterable') // 不可迭代


// 执行一下看看
for(let item of obj) console.log(item)
// ['id',107]
// ['age',18]

# 1.2 预置Iterator的数据类型

很多数据类型都预置了Iterator,称其为iterable,如下数据类型预置了Iterator:

  • Array
  • Map
  • Set
  • String(唯一的简单数据类型)
  • TypedArray
  • Arguments
  • NodeList

将以上数据类型都作一遍for...of,以及手动获取iterator实现迭代看看输出的结果

let {log} = console

// 1.Array
let arr = [1,2]
for(let item of arr){
  log(item) // 1、2
}

let iterator = arr[Symbol.iterator]()
log(iterator.next()) // {value: 1, done: false}
log(iterator.next()) // {value: 2, done: false}
log(iterator.next()) // {value: undefined, done: true}

// 2.Map
let map = new Map()
map.set('id',107)
map.set('age',18)

for(let item of map){
  log(item)// ['id', 107]、 ['age', 18]
}
for(let [key,val] of map){
  log(key,val) // 'id' 107 , 'age' 18
}

iterator= map[Symbol.iterator]()
log(iterator.next()) // {value: ["id",107],done: false}
log(iterator.next()) // {value: ["age",18],done: false}
log(iterator.next()) // {value: undefined, done: true}

// 3.Set
let set = new Set([1,2]) // 和Array一样

// 4.String
let string = 'ab'
for(let item of string){
  log(item) // a、b
}

iterator = string[Symbol.iterator]()
log(iterator.next()) // {value: 'a', done: false}
log(iterator.next()) // {value: 'b', done: false}
log(iterator.next()) // {value: undefined, done: true}

// 5.Arguments
fn(1,2)
function fn() {
  for(let item of arguments){
    log(item)// 1、2
  }
   
  iterator = arguments[Symbol.iterator]()
  log(iterator.next()) // {value: 1, done: false}
  log(iterator.next()) // {value: 2, done:false}
  log(iterator.next()) // {value: undefined, done: true}
 }

// 6.Nodelist
/** 
<ul>
  <li>1</li>
  <li>2</li>
</ul>
*/
let lis = document.getElementsByTagName('li')
for(let item of lis){
  console.log(item)// <li>1</li>、<li>2</li>
}
iterator = lis[Symbol.iterator]()
log(iterator.next()) // {value: li, done: false}
log(iterator.next()) // {value: li, done: false}
log(iterator.next()) // {value: undefined, done: true}

# 1.3调用iterator的场合

处理之前介绍的for...of会调用iterator,在其他场合也会隐式调用(本质为语法糖)

# 1.3.1 解购赋值
let set = new Set([1,2,3])
let [x,y] = set 

// 等价于
let iterator = set[Symbol.iterator]()
let x = iterator.next().value
let y = iterator.next().value
# 1.3.2 ...操作符
let str = 'abc'
let strArr = [..str]

// 等价于
let iterator = str[Symbol.iterator]()
let strArr = []
let res = iterator.next()
while(!res.done){
  strArr.push(res.value)
  res = iterator.next()
}

let arr = [1,2,3]
let [x,...y] = arr

// 等价于
let iterator = arr[Symbol.iterator]()
let x = iterator.next().value
let y = []
let res = iterator.next()
while(!res.done){
  y.push(res.value)
  res = iterator.next()
}
# 1.3.3 yield*

在generator函数中使用yiled*加上iterable数据类型可以将其作为迭代器遍历

function *f(){
  yield *[1,2,3] // 等价于 return [1,2,3][Symbol.interator]()
}
let gen = f()
gen.next() // 1
gen.next() // 2
gen.next() // 3

# 2.Generator

# 2.1什么是Generator?

Generator是一个控制Iterator的函数,称为生成器,可以随时暂停,随时恢复,它是[Symbol.iterator]的一个语法糖,执行后会自动返回一个Iterator,只是它不用依赖指定的对象

Genenrator函数有两个特点:

  • function和函数名之间有一个* ,形如function* f(){...}function * f(){...}function *f(){...}
  • 内部使用yield指定每轮迭代返回的值

不多bb,上代码

function* f(){
  yield 1
  yield 2
  yield 3
}
let iterator = f()
iterator.next() // {value:1,done:false}
iterator.next() // {value:2,done:false}
iterator.next() // {value:3,done:false} 
iterator.next() // {value:undefined,done:true}

// 相当于
function f() {
  return ({
    values: [1, 2, 3],
    [Symbol.interator]() { // Interator
      let curIdx = 0 // 当前指针
      let self = this
      return { // 迭代器对象
        next() {
          let done = curIdx > self.values.length - 1
          return { value: self.values[curIdx++], done }
        }
      }
    }
  })[Symbol.interator]()
}

# 2.2 Generator函数定义方式

Generator函数定义比较特殊,必须携带*,以下是所有定义方式

// 具名函数
function* f(){ ... }
function * f(){ ... }
function *f(){ ... }
// 匿名函数
let f = function* (){ ... }
let f = function * (){ ... }
let f = function *(){ ... }
// 对象方法
let obj={
  *f(){ ... },
  * f(){ ... },
  f: function* (){ ... },
  f: function * (){ ... },
  f: function *(){ ... },
}
// generator不能为箭头函数,会抛错 SyntaxError

# 2.3 Generator内部yieldreturn

yiled语句表示停顿,返回其后的值作为valuedonefalse,在下一轮迭代还会执行其后的语句直到遇到下一个yiledreturn

return语句表示结束,会返回其后的值作为value,并将done设置为true,随后下一轮迭代不再执行后面的语句,一直返回{value:undefined, done:true}

不多bb,来看看代码:

function *f(){
  let a=2,b=4
  yield a*b  // {value:8,done:false}
  b=5
  yield a*b // {value:10,done:false}
  a=1
  return a*b // {value:5,done:true}
  // 之后以下代码不再执行,调用next()返回的都是{value:undefined,done:true}
  a=0
  yield a*b
}

let iterator = f()
iterator.next()
iterator.next()
iterator.next()
iterator.next()
iterator.next()

流程是这样的:

next--> 执行代码1 --> yield返回value,done为false -->
next --> 执行代码2 --> yield返回value,done为false-->
next --> 执行最后的代码 --> return返回value,done为true -->
...
next--> 都返回{value:undefined,done:true}

yield还可以实现委托迭代,其后调用另一个generator函数或一个iterable数据的迭代器,通过这个方式可以实现多个生成器的串联 不多bb,上代码:

function *f(i){
  yield ++i // 2
  yield *f2(i) // 委托迭代
}
function *f2(i){
  yield ++i // 3
  yield ++i // 4
  yield* [5,6] // 委托迭代 5 6
}

let iterator = f(1)
iterator.next()
iterator.next()
iterator.next()
iterator.next()
iterator.next()

# 2.4 Generator与[Symbol.iterator]的联系

Generator函数与[Symbol.iterator]函数都返回Iterator,但是有区别的:前者返回的iterator是iterable,而后者不行

// Generator
function * generator(arr) {
  for (let i = 0,len=arr.length; i<len;i++)  yield arr[i]
}
let iterator1 = generator([1, 2])
for (let g of iterator1) {
  console.log(g) // 1 2
}

// [Symbol.iterator]
let arr = [1,2]

let iterator2 = arr[Symbol.iterator]()
for(let item of iterator2){ .... } // iterator2 is not iteratble 

两者也可以关联起来:使用Generator函数来定义[Symbol.iterator]

let obj = {
  [Symbol.iterator]:function *(){
      yield 1
      yield 2
      yield 3
  }
}
for(let item of obj){ alert(item) } // 1 2 3

# 2.5 Generator函数高级用法

# 2.5.1 控制遍历过程
function *f(arr){
  arr.forEach(item=>{
    yield item instanceof Array ? *f(item) : item
  })	
}
let arr = [1,[[2,3],4],[5,6]]
for(let item of f(arr)){ alert(item) } // 1 2 3 4 5 6

// 数组扁平化
function fn(arr){
  let res = []
  
  let interator = (function *f(arr){
    arr.forEach(item=>{
      yield item instanceof Array ? *f(item) : item
    })
  })(arr);
  
  for(let item of interator) res.push(item)
  
  return res
}
# 2.5.2 returnthrownext

iterator的return方法强制指定迭代结束,若传入参数则作为结果的value,否则为{value:undefined,done:true}

function *f(){
  yield 1
  yield 2
  yield 3
}
let gen = f()
gen.next() // 1
gen.return(107) // {value:107,done:true}
gen.next() // {value:undefined,done:true}

throw方法强制抛错,同时迭代结束

let gen2 = f()
gen2.next() // 1
gen2.throw('err') // 抛错 err
gen2.next() // {value:undefined,done:true}

在generator函数中yield这一代码行若有赋值操作,那么会先暂停返回其后的值,等下一轮next迭代开始时才会进行赋值操作, 而next()方法可以传入一个参数作为上一轮yield暂停时的返回值,不传默认为上一轮yield返回值

function* f(x) {
  let y = 2 * (yield (x + 1)) // 先yield返回,等下一轮next后才会给y赋值,由于next(12),故y=2*12=24
  let z = yield (y / 3) // 下一轮next赋值,由于next(),故z=24/3=8
  return (x + y + z) // 5+24+8
}
let b = f(5)
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }   将上次yield返回值变为12,故y = 24
b.next() // { value:37, done:true } // 5+24+8 = 42
# 2.5.3 实现斐波那契数列
function *fibonacci( pre=0, cur=1, max=Infinity ) {
  while (cur<=max) {
    yield cur
    cur = cur + pre
    pre = cur - pre
  }
}
let iterator = fibonacci()
iterator.next().value // 1
iterator.next().value // 1
iterator.next().value // 2
iterator.next().value // 3
iterator.next().value // 5
# 2.5.4 实现异步操作
let fetch = require('node-fetch');
function* request(url){
  let result = yield fetch(url) // 这里返回的是一个Promise
  alert(result) // Promise.then(res=>result = res.json)
}

let gen = request('https://api.github.com/users/github')
gen.next().value.then(res=>{ 
  gen.next(res.json()) // 回传给generator函数里的result变量
 })
 

# 参考

1.迭代器与生成器 (opens new window)

2.Iterator 和 for...of 循环(阮一峰ES6) (opens new window)

    爱自己,
    是终身浪漫的开始。
    红莲华
    x
    loading...