在了解for…of遍历的时候,简单的提到了可迭代对象这个词,这里来具体了解一下
前言 前面说到可迭代对象是含有Symbol.iterator属性的对象。具体说到可迭代对象,就要涉及到迭代器和生成器。
迭代器 迭代器本身是一个对象,这个对象是具有next()方法返回结果对象。这个返回对象是一个{value: any, done: boolean}
格式的对象。
一旦创建迭代器对象,这个对象可以通过重复调用next()方法显示地迭代。迭代一个迭代器对象,就成为消耗了这个迭代器,因为它通常只能执行一次,在产生了终值以后,调用next方法会返回一个{done: true, value: undefined}的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function createIterator (items ) { let index = 0 return { next ( ){ let result = {value : undefined , done : true } if (index < items.length ){ result = {value : items[index], done : false } index += 1 } return result } } } const it = createIterator ([1 ,2 ,3 ])console .log (it.next ()) console .log (it.next ()) console .log (it.next ()) console .log (it.next ())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function createRangeIterator (start = 0 ,end = Infinity ,step = 1 ){ let value = start const iterator = { next ( ){ let result = {value : undefined , done : true } if (value <= end){ result = {value : value, done : false } value += step } return result } } return iterator } const rit = createRangeIterator (1 , 7 , 2 )let result = rit.next () while (!result.done ){ console .log (result.value ) result = rit.next () }
每次调用迭代器的next()方法,都会返回下一个对象,直到数据集被用尽
生成器 自定义的迭代器,需要我们显示的维护其内部的状态,故需要谨慎创建。生成器函数,可以帮助我们创建一个迭代器对象
生成器函数,使用function*
语法, 返回一种成为generator的迭代器。调用生成器的下一个方法消耗值时,generator函数将执行,直到遇到yield
关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function * createRangeIterator (start = 0 , end = Infinity , step = 1 ){ for (let i = start; i <= end; i += step){ yield i } } const rit = createRangeIterator (1 , 7 , 2 )let result = rit.next ()while (!result.done ){ console .log (result.value ) result = rit.next () } const rit1 = createRangeIterator (1 , 7 , 2 )for (const item of rit1){ console .log (item) }
使用生成器函数创建的迭代器,比之前自定义的生成器精简了不少
高级生成器
生成器中next()方法也是可以接受一个参数,用于修改生成器内部的状态。传递给next()方法的参数会被yield接收。但传递给第一next()方法的参数会被忽略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function *createIterator ( ){ const param1 = yield 1 console .log ('param1' , param1) const param2 = yield 2 console .log ('param2' , param2) const param3 = yield 3 console .log ('param3' , param3) } const it = createIterator ()console .log ('result1' , it.next ('p1' )) console .log ('result2' , it.next ('p2' )) console .log ('result3' , it.next ('p3' )) console .log ('result4' , it.next ('p4' )) const it2 = createIterator ()for (const item of it2){ console .log (item) }
分析 :
关于参数接收问题, 我们可以看见每次调用next(),显示直接返回结果,拿取下一次调用next()方法中的参数作为当前yield 的返回结果 yield返回参数后,后面的代码不会执行了,只有当下次调用next()时,才会执行到下一个yield的位置 结合以上两个特性,我们可以看出它为什么丢弃了第一次的参数 可迭代对象(iterables) 为了实现可迭代 ,一个对象必须实现 @@iterator
方法,这意味着这个对象(或其原型链中的任意一个对象)必须具有一个带 Symbol.iterator
键(key)的属性。
对于只迭代一次的iterables,通常从@@iterator返回本身, 对于可以迭代多次的,必须每次调用@@iterator方法返回一个新的迭代器
常用内置可迭代对象: String 、Array 、TypedArray 、Map 、Set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const myIterable = { *[Symbol .iterator ](){ for (let i = 0 ; i < 3 ; i++){ yield i } } } for (const item of myIterable){ console .log (item) } const myIterable1 = { [Symbol .iterator ]: () => { let index = 0 const end = 3 return { next ( ){ let result = {value : undefined , done : true } if (index < end){ result = {value : index, done : false } index += 1 } return result } } } } console .log ([...myIterable1])