迭代器与生成器
迭代器是一种接口,也可以说是一种规范。它提供了一种统一的遍历数据的方法for-of。我们都知道数组、集合、对象都有自己的循环遍历方法。
支持了迭代器的数据结构才能使用for-of循环。迭代器可以提供统一的遍历数据的方式,只要在想要遍历的数据结构中添加一个支持迭代器的属性即可。这个属性写法是这样的:
1 const obj = { 2 [Symbol.iterator]:function(){} 3 }
Symbol.iterator
属性对应一个函数,执行后返回当前对象的遍历器对象。
在JS中,默认支持迭代器的结构有:
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
显然自定义对象不包含在其中,自然也不能使用for-of:
1 let obj ={ 2 name:'郑大侠', 3 age:'22', 4 gender:'男', 5 introduce:function(){ 6 console.log('我是'+this.name); 7 } 8 9 } 10 11 for(let attr of obj){ 12 console.log(attr); //Uncaught TypeError: obj is not iterable 13 }
数组就可以:
1 let arr=[1,2,3] 2 for(let a of arr){ 3 console.log(a); // 1 2 3 4 }
迭代器属性[Symbol.iterator]可执行来返回一个对象
该属性返回的对象上有next()方法,
迭代器的遍历方法是首先获得一个迭代器的指针,初始时该指针指向第一条数据之前。接着通过调用next方法,改变指针的指向,让其指向下一条数据。每一次的next都会返回一个对象,该对象有两个属性。其中value代表想要获取的数据,done是个布尔值,false表示当前指针指向的数据有值。true表示遍历已经结束。
以含有迭代器属性的数组为例:
1 let arr=[1,2,3] 2 let a = arr[Symbol.iterator]() 3 console.log(a.next()); 4 console.log(a.next()); 5 console.log(a.next());
要使得自定义对象也可以迭代遍历,可以通过手动添加迭代器完成:
1 let obj ={ 2 name:'郑大侠', 3 age:'22', 4 gender:'男', 5 introduce:function(){ 6 console.log('我是'+this.name); 7 }, 8 [Symbol.iterator]:function(){ 9 let i =0 10 // 获取当前对象并存入一个数组里 11 let keys = Object.keys(this) 12 // 返回一个next方法 13 return { 14 next(){ 15 return{ 16 value:keys[i++], //每次遍历得到第i个属性 17 done:i>keys.length //遍历完成则返回false 18 } 19 } 20 } 21 } 22 } 23 let o = obj[Symbol.iterator]() 24 console.log(o.next()); 25 console.log(o.next()); 26 console.log(o.next()); 27 console.log(o.next()); 28 // 完成 29 for(let attr of obj){ 30 console.log(attr); 31 }
生成器(Generator)
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。调用它返回迭代器对象
形式上的定义:
1 function* say(){} //这个”*”只能写在function关键字的后面。 2 const say = function*(){}
普通函数在调用后,必然开始执行该函数,直到函数执行完或遇到return为止。中途是不可能暂停的。但是生成器函数则不一样,它可以通过yield关键字将函数的执行挂起,或者理解成暂停。它的外部在通过调用next方法,让函数继续执行,直到遇到下一个yield,或函数执行完毕。
调用say函数,这句和普通函数的调用没什么区别。但是此时say函数并没有执行,而是返回了一个该生成器的迭代器对象。接下来就和之前一样,执行next方法,say函数执行,当遇到yield时,函数被挂起,并返回一个对象对象中包含value属性,它的值是yield后面跟着的数据。并且done的值为false。再次执行next,函数又被激活,并继续往下执行,直到遇到下一个yield。当所有的yield都执行完了,再次调用next时得到的value就是undefined,done的值为true。
1 function* say() { 2 yield "开始"; 3 yield "执行中"; 4 yield "结束"; 5 } 6 let it = say(); // 调用say方法,得到一个迭代器 7 console.log(it.next()); // { value: '开始', done: false } 8 console.log(it.next()); // { value: '执行中', done: false } 9 console.log(it.next()); // { value: '结束', done: false } 10 console.log(it.next()); // { value: undefined, done: true }
它的yield,其实就是next方法执行后挂起的地方,并得到你返回的数据。
那么这个生成器有什么用呢?它的yield关键字可以将执行的代码挂起,外部通过next方法让它继续运行,这和异步操作的原理非常类似。
参考:https://zhuanlan.zhihu.com/p/66593213