迭代器Iterator

迭代器

是确使用户可在容器对象(container,例如链表或数组)上遍访的对象,使用该接口无需关心对象的内部实现细节

JavaScript迭代器标准

JavaScript中,迭代器是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol)

那么在JavaScript中,这个标准就是一个特定的next方法

next方法要求

next方法为无参数或一个参数的函数,返回一个拥有以下两个属性的对象:

  1. done(boolean)
    如果迭代器可以产生序列中的下一个值,则为false
    如果迭代器已将序列迭代完毕则为true(此时value属性可选)

  2. value
    迭代器返回的任何JavaScript值,done为true时可省略

  1. const arr=['123','456','789'];
  2. /* 迭代器案例 */
  3. function createArrayIterator(arr){
  4. let index=0;
  5. return{
  6. next(){
  7. if(index<arr.length){
  8. return {done:false,value:arr[index++]};
  9. }else{
  10. return {done:true,value:undefined};
  11. }
  12. }
  13. }
  14. }
  15. const arrIterator=createArrayIterator(arr);
  16. console.log(arrIterator.next()) // {done:false,value:'123'}
  17. console.log(arrIterator.next()) // {done:false,value:'456'}
  18. console.log(arrIterator.next()) // {done:false,value:'789'}
  19. console.log(arrIterator.next()) // {done:true,value:undefined}

可迭代对象

它与迭代器不同,当一个对象实现了可迭代协议(iterable protocol)时,它就是一个可迭代对象

这个对象的要求是必须实现@@iterator方法,在代码中我们使用Symbol.iterator访问该属性,且该方法返回迭代器

  1. /* 可迭代对象 */
  2. const iterableObj={
  3. arr:['123','456','789'],
  4. [Symbol.iterator]:function(){
  5. let index=0;
  6. return{
  7. next:()=>{
  8. if(index<this.arr.length){
  9. return {done:false,value:this.arr[index++]};
  10. }else{
  11. return {done:true,value:undefined};
  12. }
  13. }
  14. }
  15. }
  16. }
  17. const iterator=iterableObj[Symbol.iterator]();
  18. console.log(iterator.next()) // {done:false,value:'123'}
  19. console.log(iterator.next()) // {done:false,value:'456'}
  20. console.log(iterator.next()) // {done:false,value:'789'}
  21. console.log(iterator.next()) // {done:true,value:undefined}
  22. for(const value of iterableObj){
  23. console.log(value); // '123' '456' '789'
  24. }

内置可迭代对象

Array

  1. const arr=[1,2,3]
  2. console.log(arr[Symbol.iterator]) // function

Map/Set

  1. const set=new Set()
  2. for(const item of set){
  3. console.log(item)
  4. }

函数arguments对象

  1. function foo(){
  2. console.log(arguments[Symbol.iterator]) // function
  3. for(const argument of arguments){
  4. console.log(argument) // 10 20 30
  5. }
  6. }
  7. foo(10,20,30)

可迭代对象的应用

  • JavaScript语法

    • for of

    • 展开运算符(spread syntax) ```javascript / 正因为是可迭代对象,才可使用展开语法 / const arr=[1,2,3] const newArr=[…arr,…iterableObj]

/ 但对于普通对象,其为不可迭代对象,却依旧可以使用展开运算符 这是因为这是ES9(ECMA2018)中新增特性,其使用的并不是迭代器,在V8引擎实现该特性是通过遍历对象的key和value再赋值处理 / const obj={name:’rv’,age:20} const newObj={…obj}

  1. -
  2. 解构语法
  3. ```javascript
  4. /* 数组的解构赋值也是利用的迭代器 */
  5. const [a,b]=[1,2,3]
  6. /* 同上ES9新增特性,普通对象也支持解构语法跟是否为可迭代对象无关,其实现原理不同 */
  • 创建一些对象时
    1. /* 在创建Set对象时可传入可迭代对象 */
    2. const set=new Set([1,2,3]);
    3. /* Set(iterable?: Iterable<any>):Set<any> */
  • new Map([iterbale])
  • new WeakMap([iterable])
  • new Set([iterable])
  • new WeakSet([iterable])

    • 一些方法的调用
  • Promise.all(iterable)

  • Promise.race(iterable)
  • Array.from(iterable)

迭代器return方法

可用于拦截拦截器的提前终止操作

  1. const iterator={
  2. next(){···},
  3. return(){
  4. console.log('迭代器提前终止')
  5. /* 需有该返回值 */
  6. return {done:true,value:undefined}
  7. }
  8. }

生成器Generator

生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活地控制函数什么时候继续执行、暂停执行等

生成器事实上是一种特殊的迭代器

生成器函数

生成器函数也是一种函数,但和普通函数有一些区别

  • 生成器函数需要在function后添加符号*
  • 生成器函数可以通过yield关键字来控制函数的执行流程
  • 生成器函数返回值是一个生成器(Generator)
  1. /* '*'符号的位置没有规定 */
  2. function* foo0(){···}
  3. function * foo1(){···}
  4. function *foo2(){···}
  5. /* 生成器函数案例 */
  6. function* foo(){
  7. console.log('函数执行开始')
  8. console.log(100)
  9. yield
  10. console.log(200)
  11. yield
  12. console.log(300)
  13. yield
  14. console.log('函数执行结束')
  15. }

调用生成器函数

调用生成器函数并不会执行函数体代码,只会返回一个生成器,需要使用生成器才能执行函数体代码

  1. const generator=foo()

生成器的使用

类似迭代器,每次调用next方法则会执行一段代码,但当遇到return时,其会直接停止生成器

  1. generator1.next() // '函数执行开始' 100
  2. generator1.next() // 200
  3. generator1.next() // 300
  4. generator1.next() // '函数执行结束'

生成器next方法

next方法的返回值

生成器next方法的返回值同iterator相同,为含有value和done属性的对象

但在不同的情况下next方法的返回对象的value值不同

  • yield
    仅是yield关键字,返回{done:false,value:undefined}
  • yield 语法/值
    此时会执行该语法并返回返回值{done:false,value:returnValue}
  • return
    生成器遇到return关键字直接停止生成器,返回{done:true:value:returnValue}
  1. const generator=foo()
  2. consolle.log(generator.next()) // {done:false,value:undefined}
  3. consolle.log(generator.next()) // {done:false,value:undefined}
  4. consolle.log(generator.next()) // {done:false,value:undefined}
  5. consolle.log(generator.next()) // {done:true,value:undefined}

next方法传入参数

生成器的next方法可以在调用的同时传入参数,并在函数体中通过yield关键字的返回值获取并使用

  1. function bar(){
  2. console.log('函数开始执行')
  3. const value1=yield
  4. console.log(value1)
  5. console.log('函数结束执行')
  6. }
  7. const generator=bar()
  8. generator.next('abc') // '函数开始执行'
  9. generator.next() // 'abc' '函数结束执行'

生成器return方法

return方法可以同next方法一样调用,但并不会执行下一段代码,而是提前终止生成器函数代码,

return方法一样可以传入参数,但参数会直接作为返回值value返回

  1. const generator=foo()
  2. consolle.log(generator.next()) // {done:false,value:undefined}
  3. consolle.log(generator.return(10)) // {done:true,value:10}

生成器throw方法

throw方法可以同next方法一样调用,其效果和return方法类似,并不会执行下一段代码的同时会抛出错误

但并不一定会提前终止生成器函数,在yield关键字处捕获错误的情况下,依旧可以执行之后的生成器函数代码

throw方法一样可以传入参数,参数作为错误信息

yield*

yield* <iterableObj>其后接可迭代对象,效果等同于for循环yield

生成器替代迭代器

  1. const arr=['123','456','789'];
  2. /* 迭代器 */
  3. function createArrayIterator(arr){
  4. let index=0;
  5. return{
  6. next(){
  7. if(index<arr.length)
  8. return {done:false,value:arr[index++]};
  9. else
  10. return {done:true,value:undefined};
  11. }
  12. }
  13. }
  14. /* 生成器代替迭代器 */
  15. function* createArrayIterator(arr){
  16. // 方式一
  17. for(const itme of arr){
  18. yield item;
  19. }
  20. // 方式二
  21. yield* arr;
  22. }
  23. class ClassRoom{
  24. constructor(students){
  25. this.students=students
  26. }
  27. // 方式三
  28. [Symbol.iterator]=function*(){
  29. yield* this.students
  30. }
  31. // 方式四
  32. *[Symbol.iterator](){
  33. yield* this.students
  34. }
  35. }