除了Array,ES6还在JavaScript中新增了Map和Set结构。当我们需要对这些数据进行操作时,我们需要一个统一的接口来处理这些差异。数据结构。ES6中新加入的Iterator(迭代器)提供了这样的机制。Symbol.iteratorES6支持的数据结构提供了Symbol.iterator方法,返回一个迭代器对象。目前,Array、Set、Map等数据结构默认都有Symbol.iterator属性。如下图,可以看到Object类型是No]());//[MapEntries]{}控制台。log((newSet())[Symbol.iterator]());//[SetIterator]{}console.log({}[Symbol.iterator]);//undefined除了上面提到的数据结构,还有一些在JavaScriptArray-like对象默认也有一个Symbol.iterator属性,例如strings、arguments对象、DOM的NodeList对象。Stringconststr='nodejs';console.log(str[Symbol.iterator]());//对象[StringIterator]{}for(constvalofstr){console.log(val);//nodejs}argumentsobjectfunctionprint(){console.log(arguments[Symbol.iterator]());//Object[ArrayIterator]{}for(constvalofarguments){console.log(val);//node}}print('n','o','d','e')DOMNodeListobjectconstdivNodeList=document.getElementsByTagName('div')console.log(divNodeList[Symbol.iterator]())//ArrayIterator{}for(constdivofdivNodeList){//会输出每个divtagconsole.log(div);}迭代器对象的next方法调用可迭代对象的Symbol.iterator方法返回一个迭代器对象,其接口有一个next方法,返回value和done属性,其中value属性为当前成员的值,done属性表示遍历是否结束。你可能对生成器函数(Generator)并不陌生。同样,当你执行一个生成器函数时,你也会得到一个迭代器对象,但是生成器和迭代器的区别并不是一个概念。constarr=['N','o','d','e'];constiterator=arr[Symbol.iterator]();console.log(iterator.next());//{value:'N',done:false}console.log(iterator.next());//{value:'o',done:false}console.log(iterator.next());//{value:'d',done:false}console.log(iterator.next());//{value:'e',done:false}console.log(iterator.next());//{value:undefined,done:true}中例子,声明一个数组arr,调用arr的Symbol.iterator方法创建一个iterator对象iterator,然后不断调用next方法返回当前数组内容,直到next方法的返回值为true,数组访问完成了。Iterator接口在遍历解构赋值数组、Set、Map解构赋值时,默认会调用Symbol.iterator方法。注意Map调用Symbol.iterator方法返回一个entries方法,返回一个新的迭代器对象,包含Map对象中每个元素按插入顺序排列的[key,value]数组,所以调用Map实例的keys或values方法也返回一个新的迭代器对象。constset=newSet().add('n').add('o');constmap=newMap().set('d').set('e');const[xSet,ySet]=set;console.log(xSet,ySet)//noconst[xMap,yMap]=map.keys();console.log(xMap,yMap)//de扩展操作符ES6中的扩展操作符(...)也会默认调用数组、集合、映射等结构的Symbol.iterator方法constset=newSet('node');const[x,y,...z]=set;console.log(x,y,z);//没有['d','e']for...of循环ES6借鉴了C++、Python等语言引入了for...of循环,在循环内部也会调用Symbol.iterator方法,只要有Iterator接口的数据结构都可以使用.constset=newSet().add('n').add('o');for(constvalofset){console.log(val);}for...of循环在执行时也可以使用break;中断迭代器执行。以下示例修改循环语句以在第一次执行val等于n后执行break。for(constvalofset){console.log(val);//nif(val==='n')break;}其他方法数组默认支持Iterator接口,所以任何接收数组作为参数的方法也会调用Symbol默认使用.iterator方法,像这样:constset=newSet().add('n').add('o');console.log(Array.from(set));//['n','o']Promise.all(set).then(val=>console.log(val))//['n','o']Promise.race(set).then(val=>console.log(val))//n自定义iterator迭代协议是指iterable协议。要成为可迭代对象,首先要有一个**@@iterator**(Symbol.iterator)属性,它是一个无参数的函数,返回一个匹配iterator协议的Objects的函数。根据迭代器协议定义,这个迭代器对象应该返回一个next()方法,这个next()方法返回一个包含value和done属性的对象。constmyIterator={//for...of循环会使用[Symbol.iterator]:function(){returnthis},//标准迭代器接口方法next:function(){//...}}如果使用了写法TypeScript的描述如下://遍历接口IterableinterfaceIterable{[Symbol.iterator]:Iterator}//迭代器对象interfaceIterator{next(value?:any):IterationResult,}//next方法返回值定义interfaceIterationResult{value:any,done:boolean}基于普通函数的迭代器实现迭代器的函数实现可以是普通函数,也可以是生成器函数。我们以一个普通的函数为例,定义一个Range构造函数来输出两个值。该区域的所有值。functionRange(start,end){this.id=start;this.end=end;}Range.prototype[Symbol.iterator]=function(){returnthis}Range.prototype.next=functionnext(){if(this.id>this.end){return{value:undefined,done:true}}return{value:this.id++,done:false}}constr1=newRange(0,3);constit=r1[Symbol.iterator]()for(constidofr1){console.log(id);//0,1,2,3}基于生成器函数的迭代器实现最容易使用生成器函数(Generator),每次使用yield语句返回即可价值。如下图:Range.prototype[Symbol.iterator]=function*(){while(this.id<=this.end){yieldthis.id++;}}异步迭代器至此我们已经讲解了上面所有同步模式下的迭代器,这个很好理解,因为我们的数据源本身是同步的,但是在Node.js中,网络I/O请求或者文件I/O请求,都是基于事件的异步,所以我们不能用它我们使用Symbol.iterator的方式。ECMAScript2018标准中提供了**Symbol.asyncIterator**属性,它是一个异步迭代器。如果一个对象设置了这个属性,它就是一个异步可迭代对象。相应的,我们需要使用forawait...of循环遍历数据。CustomasynchronousiteratorfunctionRange(start,end){this.id=start;this.end=end;}//与上面不同的是,我们在functionRange.prototype[Symbol.asyncIterator]=asyncfunction*()前添加了async关键字{while(this.id<=this.end){yieldthis.id++;}}constr1=newRange(0,3);console.log(r1[Symbol.asyncIterator]());//对象[AsyncGenerator]{}forawait(constidofr1){console.log(id);//0,1,2,3}不同于同步迭代器。同步迭代器返回一个常规的{value,done}对象,而异步迭代器返回的是一个包含{value,done}的Promise对象。同步可迭代协议具有Symbol.iterator属性,异步可迭代协议具有Symbol.asyncIterator属性。同步迭代器使用for...of循环遍历,异步迭代器使用forawait...of循环遍历。异步迭代器支持目前没有默认设置[Symbol.asyncIterator]属性的JavaScript内置对象。不过,WHATWG(WebHypertextApplicationTechnologyWorkingGroup)Streams将被定为第一批异步可迭代对象,并且[Symbol.asyncIterator]最近已经登陆设计规范。下一节我们将讲解Node.js中异步迭代器的使用,欢迎关注。参考[1]你所不知道的JavaScript(中卷):https://book.douban.com/subject/26854244/[2]可迭代协议:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols[3]Symbol.asyncIterator:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator本文转载自微信公众号《Nodejs技术栈》,可以通过以下二维码关注。转载本文请联系Nodejs技术栈公众号。
