当前位置: 首页 > 科技观察

JavaScriptLazy评测:Iterable对象和迭代器

时间:2023-03-16 21:34:49 科技观察

本文已获得原作者MelkorNemesis授权翻译。惰性求值惰性求值常译为“延迟计算”或“惰性计算”,意思是只在真正需要执行时才计算表达式的值。惰性求值的对立面是早期求值(eagerevaluation),也称为贪心求值(greedyevaluation)或严格求值,是大多数传统编程语言的求值策略。充分利用lazyevaluation的特点带来的好处主要体现在以下两个方面:避免不必要的计算,带来性能提升。节省空间并使无限循环数据结构成为可能。迭代器ES6中的迭代器支持惰性求值和创建用户定义的数据序列。迭代是一种遍历数据的机制。迭代器是用于迭代数据结构(称为Iterable)的元素的指针,该指针用于生成值序列。迭代器是可以迭代的对象。它抽象数据容器,使它们表现得像可迭代对象。迭代器在实例化时不计算每个项目的值,仅在请求时产生下一个值。这非常有用,特别是对于大型数据集或具有无限元素的序列。可迭代对象可迭代对象是一种数据结构,其元素旨在公开访问。JS中很多对象都是可迭代的,可能不太好理解,但是仔细观察就会发现迭代的特点:newMap([iterable])newWeakMap([iterable])newSet([iterable]])newWeakSet([iterable])Promise.all([iterable])Promise.race([iterable])Array.from([iterable])还需要一个可迭代对象,否则会抛出类型错误,例如:for...of...(展开运算符)const[a,b,..]=iterable(解构赋值)yield*(generator)JavaScript中已经有很多内置的iterables:String,Array,TypedArray,Map,Set.迭代协议迭代器和可迭代对象遵循迭代协议。协议是一组接口并指定如何使用它们。Iterator遵循Iterator协议,Iterable遵循Iterable协议。可迭代协议一个对象要成为可迭代的,它必须通过Symbol.iterator实现一个迭代器方法,它是迭代器的工厂。使用TypeScript,Iterable协议看起来像这样:interfaceIterable{[Symbol.iterator]():Iterator;}Symbol.iterator]()是一个无参数函数。在可迭代对象上调用它意味着我们可以通过this访问可迭代对象,它可以是常规函数或生成器函数。迭代器协议迭代器协议定义了生成值序列的标准方法。为了使对象成为迭代器,它必须实现next()方法。迭代器可以实现return()方法,我们将在本文后面讨论。使用TypeScript,迭代器协议如下所示:interfaceIterator{next():IteratorResult;return?(value?:any):IteratorResult;}IteratorResult定义如下:interfaceIteratorResult{value?:any;done:boolean;}consumption迭代器是否已经使用,false表示还有值需要生成,true表示迭代器已经结束。value可以是任何JS值,它是显示给消费者的值。当done为真时,可以省略值。Combinediterators和iterableobjects可以用下图来表示:案例的基础知识介绍完了,接下来我们通过一些案例来深化我们的形象。范围迭代器让我们从一个非常基本的迭代器开始,createRangeIterator迭代器。我们手动调用it.next()来获取下一个IteratorResult。最后一次调用返回{done:true},这意味着现在使用迭代器并且不再产生任何值。函数createRangeIterator(from,to){leti=from;return{next(){if(i<=to){return{value:i++,done:false};}else{return{done:true};}}}}constit=createRangeIterator(1,3);console.log(it.next());console.log(it.next());console.log(it.next());console.log(it.next());Iterablerangeiterators在本文前面,我提到JS中的某些语句需要一个可迭代对象。因此,我们前面的示例在与for...of循环一起使用时将不起作用。但是创建符合迭代器和可迭代协议的对象非常容易。函数createRangeIterator(from,to){leti=fromreturn{[Symbol.iterator](){returnthis},next(){if(i<=to){return{value:i++,done:false}}else{return{done:true}}}}}constit=createRangeIterator(1,3)for(constofit){console.log(i)}无限序列迭代器迭代器可以表示无限大小的序列,因为它们只在需要时计算值。注意不要在无限迭代器上使用扩展运算符(...),JS会尝试消耗迭代器,这永远不会结束,因为迭代器是无限的。所以你的应用程序会因为内存耗尽而崩溃??此外,for...of循环是相同的,因此请确保您可以退出循环:functioncreateEvenNumbersIterator(){letvalue=0return{[Symbol.iterator](){returnthis},next(){value+=2return{value,done:false}}}}constit=createEvenNumbersIterator()const[a,b,c]=itconsole.log({a,b,c})const[x,y,z]=itconsole.log({x,y,z})for(constevenofit){console.log(even)if(even>20){break}}关闭迭代器正如我们前面提到的,迭代控制器可以选择性地使用return()方法。当迭代器没有迭代到最后时使用这个方法,让迭代器清理。for...of循环可以通过以下方式提前终止迭代:breakcontinuethrowreturnfunctioncreateCloseableIterator(){letidx=0constdata=['a','b','c','d','e']functioncleanup(){console.log('Performingcleanup')}return{[Symbol.iterator](){returnthis},next(){if(idx<=data.length-1){return{value:data[idx++],done:如果(value==='c'){break}}console.log('\n--------\n')const_it=createCloseableIterator();for(constvalueof_it){console.log(值);}如果已知迭代器已经结束,则手动调用cleanup()函数。如果突然完成,return()会为我们工作并清理。额外内容如果您已经做到了这一点,让我们来看看一些额外内容。组合器组合器是组合现有可迭代对象以创建新可迭代对象的函数。因此,我们能够创建许多效用函数。地图或过滤器呢?看看下面的代码,花点时间理解它。functioncreateEvenNumbersIterator(){letvalue=0;return{[Symbol.iterator](){returnthis;},next(){value+=2;return{value,done:false};}}}functionmap(fn,iterable){constiter=iterable[Symbol.iterator]();return{[Symbol.iterator](){returnthis;},next(){constn=iter.next();if(!n.done){return{value:fn(n.value),done:false};}else{return{done:true};}}}}functionfilter(fn,iterable){constiter=iterable[Symbol.iterator]();return{[Symbol.iterator](){returnthis;},next(){constn=iter.next();if(!n.done){if(fn(n.value)){return{value:n.value,done:false};}else{returnthis.next();}}else{return{done:true};}}}}functiontake(n,iterable){constiter=iterable[Symbol.iterator]();return{[Symbol.iterator](){returnthis;},next(){if(n>0){n--;returniter.next();}else{return{done:true};}}}}functioncycle(iterable){constiter=iterable[符号.iterator]();constsaved=[];letidx=0;return{[Symbol.iterator](){returnthis;},next(){constn=iter.next();if(!n.done){saved[idx++]=n.value;return{value:n.value,done:false};}else{return{value:saved[idx++%saved.length],done:false};}}}}functioncollect(iterable){//consumestheiteratorreturnArray.from(iterable);}constevenNumbersIterator=createEvenNumbersIterator();constresult=collect(//7.andcollecttheresultfilter(//??6.keeponlyvalueshigherthan1val=>val>1,map(//??5.divideobtainedvaluesby2val=>val/2,take(//??4.takeonlysixofthem6,cycle(//??3.makeaninfinitecyclingsequenceofthemtake(//??2.takejustthreeofthem3,evenNumbersIterator//??1.infinitesequenceofevennumbers))))));控制台日志(结果);这是很多代码,我们很快就会看到如何使用生成器。请继续关注用函数式编程概念重构所有这些代码,并留意我的后续文章,我们还有很多内容要介绍。作者:MelkorNemesis译者:前端小智来源:medium原文:https://medium.com/@MelrNemesis/javascript-lazy-evaluation-iterables-iterators-e0770a5de96f关注下方二维码。转载请联系大千世界公众号。