1. 首页
  2. IT资讯

「ES6基础」迭代器(iterator)

“u003Cdivu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F72ea7c533641444881cf8e03fe3b61e5″ img_width=”899″ img_height=”600″ alt=”「ES6基础」迭代器(iterator)” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E迭代器(iterator)是一个结构化的模式,用于从源以一次一个的方式提取数据。迭代器的使用可以极大地简化数据操作,于是ES6也向JS中添加了这个迭代器特性。新的数组方法和新的集合类型(如Set集合与Map集合)都依赖迭代器的实现,这个新特性对于高效的数据处理而言是不可或缺的,在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(…),甚至连异步编程都可以使用迭代器。u003Cu002Fpu003Eu003Cpu003E今天笔者将从以下几个方面进行介绍迭代器:u003Cu002Fpu003Eu003Culu003Eu003Cliu003E什么是迭代器(iterator)?u003Cu002Fliu003Eu003Cliu003E基于协议实现迭代器u003Cu002Fliu003Eu003Cliu003E迭代器的应用u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E本篇文章阅读时间预计u003Cstrongu003E6u003Cu002Fstrongu003E分钟。u003Cu002Fpu003Eu003Ch1u003Eu003Cstrongu003E迭代器(iterator)u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F651ce6f2779e4405b03c219310b0cc80″ img_width=”1168″ img_height=”390″ alt=”「ES6基础」迭代器(iterator)” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E迭代器是一种有序、连续的、基于拉取的用于消耗数据的组织方式,用于以一次一步的方式控制行为。简单的来说我们迭代循环一个可迭代对象,不是一次返回所有数据,而是调用相关方法分次进行返回。u003Cu002Fpu003Eu003Cpu003E迭代器iterator是一个object,这个object有一个next函数,该函数返回一个有value和done属性的object,其中value指向迭代序列中当前next函数定义的值。u003Cu002Fpu003Eu003Cpu003EES6的迭代协议分为迭代器协议(iterator protocol)和可迭代协议(iterable protocol),迭代器基于这两个协议进行实现。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E迭代器协议:u003Cu002Fstrongu003E iterator协议定义了产生value序列的一种标准方法。只要实现符合要求的next函数,该对象就是一个迭代器。相当遍历数据结构元素的指针,类似数据库中的游标。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E可迭代协议:u003Cu002Fstrongu003E 一旦支持可迭代协议,意味着该对象可以用for-of来遍历,可以用来定义或者定制JS 对象的迭代行为。常见的内建类型比如Array & Map都是支持可迭代协议的。对象必须实现@@iterator方法,意味着对象必须有一个带有@@iterator key的可以通过常量Symbol.iterator访问到的属性。u003Cu002Fpu003Eu003Cpu003E下图展示了arrays,Maps,Strings数据类型实现了可迭代协议,我们可以使用for-of和展开语法显示迭代器的数据。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F435278e45d804fa9a4f31ed67f096729″ img_width=”1287″ img_height=”433″ alt=”「ES6基础」迭代器(iterator)” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Ch1u003Eu003Cstrongu003E基于协议实现迭代器u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cstrongu003E迭代器协议u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E如下代码展示了基于迭代协议进行实现:u003Cu002Fpu003Eu003Cpreu003Elet obj = {u003Cbru003E array: [1, 2, 3, 4, 5],u003Cbru003E nextIndex: 0,u003Cbru003E next: function() {u003Cbru003E return this.nextIndex < this.array.length ? {value: this.array[this.nextIndex++], done: false} : {done: true}u003Cbru003E }u003Cbru003E};u003Cbru003Econsole.log(obj.next().value);u003Cbru003Econsole.log(obj.next().value);u003Cbru003Econsole.log(obj.next().value);u003Cbru003Econsole.log(obj.next().value);u003Cbru003Econsole.log(obj.next().value);u003Cbru003Econsole.log(obj.next().done);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E上述代码将会输出u003Cu002Fpu003Eu003Cpreu003E1u003Cbru003E2u003Cbru003E3u003Cbru003E4u003Cbru003E5u003Cbru003Etrueu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E上述代码的next方法还可以按如下代码进行改写,看起来更清晰些:u003Cu002Fpu003Eu003Cpreu003Eif(this.nextIndex < this.array.length) {u003Cbru003E this.nextIndex++;u003Cbru003E return { value: this.array[this.nextIndex], done: false }u003Cbru003E} else {u003Cbru003E return { done: true }u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E我们可以看出,next方法的实现,如果存在新的元素,返回当前元素的并将当前元素位置的标识递增加1,当没有元素时,返回{ done: true }。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E可迭代协议u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E根据可迭代协议,对象需要提供@@iterator方法; 也就是说,它必须将Symbol.iterator符号作为属性键。 @@iterator方法必须返回迭代器对象。代码实现如下:u003Cu002Fpu003Eu003Cpreu003Elet obj = {u003Cbru003E array: [1, 2, 3, 4, 5],u003Cbru003E nextIndex: 0,u003Cbru003E [Symbol.iterator]: function(){u003Cbru003E return {u003Cbru003E array: this.array,u003Cbru003E nextIndex: this.nextIndex,u003Cbru003E next: function(){u003Cbru003E return this.nextIndex < this.array.length ?u003Cbru003E {value: this.array[this.nextIndex++], done: false} :u003Cbru003E {done: true};u003Cbru003E }u003Cbru003E }u003Cbru003E }u003Cbru003E};u003Cbru003Elet iterable = obj[Symbol.iterator]()u003Cbru003Econsole.log(iterable.next().value);u003Cbru003Econsole.log(iterable.next().value);u003Cbru003Econsole.log(iterable.next().value);u003Cbru003Econsole.log(iterable.next().value);u003Cbru003Econsole.log(iterable.next().value);u003Cbru003Econsole.log(iterable.next().done);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E上述代码将会输出u003Cu002Fpu003Eu003Cpreu003E1u003Cbru003E2u003Cbru003E3u003Cbru003E4u003Cbru003E5u003Cbru003Etrueu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E上述代码,我们实现了自定义的迭代器,基于JS的作用域和闭包特性才能轻松实现。arrays,Maps,Strings数据类型实现了可迭代协议,其 __proto__原型链指向自带Symbol.iterator的方法,节省了我们手写代码的时间,如下代码所示:u003Cu002Fpu003Eu003Cpreu003Econst arr = [1, 2];u003Cbru003Econst iterator = arr[Symbol.iterator](); u002Fu002F returns you an iteratoru003Cbru003Econsole.log(iterator.next())u003Cbru003Econsole.log(iterator.next())u003Cbru003Econsole.log(iterator.next())u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E上述代码将会输出:u003Cu002Fpu003Eu003Cpreu003E{ value: 1, done: false }u003Cbru003E{ value: 2, done: false }u003Cbru003E{ value: undefined, done: true }u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E我们可以使用for-of,展开语法迭代数组,示例代码如下:u003Cu002Fpu003Eu003Cpreu003Econst arr = [1, 2];u003Cbru003Efor(var v of arr){u003Cbru003E console.log(v);u003Cbru003E}u003Cbru003Eu002Fu002Foutputs 1u003Cbru003Eu002Fu002Foutputs 2u003Cbru003Econsole.log([…arr]);u003Cbru003Eu002Fu002Foutputs[1,2];u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eobj对象没有实现可迭代协议,我们如何迭代obj对象呢?实现obj的迭代器呢,示例代码如下:u003Cu002Fpu003Eu003Cpreu003Evar obj={u003Cbru003E a:1,u003Cbru003E b:2,u003Cbru003E c:3,u003Cbru003E [Symbol.iterator]:function () {u003Cbru003E var keys=Object.keys(this);u002Fu002Fobject.vulues(this)u003Cbru003E var index=0;u003Cbru003E return{u003Cbru003E next:()=>u003Cbru003E (index<keys.length)?u003Cbru003E {value: this[keys[index++]], done:false} :{done: true,value:undefined}u003Cbru003E }u003Cbru003E }u003Cbru003E};u003Cbru003Econsole.log([…obj]);u003Cbru003Eu002Fu002Foutputs [1,2,3]u003Cbru003Eu003Cu002Fpreu003Eu003Ch1u003Eu003Cstrongu003E迭代器应用u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cstrongu003E斐波那契数列u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)。u003Cu002Fpu003Eu003Cpu003E我们可以使用迭代器来产生一个序列数不大于100的斐波那契数列,示例代码如下:u003Cu002Fpu003Eu003Cpreu003Elet Fib= {u003Cbru003E [Symbol.iterator](){u003Cbru003E let n1=1;u003Cbru003E let n2=1;u003Cbru003E let max=100;u003Cbru003E return {u003Cbru003E next(){u003Cbru003E let current=n2;u003Cbru003E n2=n1;u003Cbru003E n1=n1+current;u003Cbru003E if(current<max){u003Cbru003E return {value: current, done: false}u003Cbru003E }else{u003Cbru003E return { done: true}u003Cbru003E }u003Cbru003E }u003Cbru003E }u003Cbru003E }u003Cbru003E}u003Cbru003Econsole.log([…Fib]);u003Cbru003Eu002Fu002Foutputs [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ]u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E模拟任务队列u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E有时候我们需要要执行任务放在一个队列里,分次执行,我们可以使用迭代器进行模拟,示例代码如下:u003Cu002Fpu003Eu003Cpreu003Elet tasks={u003Cbru003E actions:[],u003Cbru003E [Symbol.iterator](){u003Cbru003E let steps=this.actions.slice();u003Cbru003E return {u003Cbru003E [Symbol.iterator]() {return this;},u003Cbru003E next(…args){u003Cbru003E if(steps.length>0){u003Cbru003E let res=steps.shift()(…args);u003Cbru003E return {value:res,done:false};u003Cbru003E }u003Cbru003E else{u003Cbru003E return {done:true}u003Cbru003E }u003Cbru003E },u003Cbru003E return(v){u003Cbru003E steps.length=0;u003Cbru003E return {value:v,done:true};u003Cbru003E }u003Cbru003E };u003Cbru003E }u003Cbru003E};u003Cbru003Etasks.actions.push(u003Cbru003E function step1(x) {u003Cbru003E console.log(“step 1:”,x);u003Cbru003E return x*2;u003Cbru003E },u003Cbru003E function step2(x,y) {u003Cbru003E console.log(“step 2:”,x,y);u003Cbru003E return x+(y*2);u003Cbru003E },u003Cbru003E function step3(x,y,z) {u003Cbru003E console.log(“step 3:”,x,y,z);u003Cbru003E return (x*y)+z;u003Cbru003E }u003Cbru003E);u003Cbru003Elet it=tasks[Symbol.iterator]();u003Cbru003Econsole.log(it.next(10));u003Cbru003Econsole.log(it.next(20,50));u003Cbru003Econsole.log(it.next(20,50,120));u003Cbru003Econsole.log(it.next());u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E上述代码输出:u003Cu002Fpu003Eu003Cpreu003Estep 1: 10u003Cbru003E{ value: 20, done: false }u003Cbru003Estep 2: 20 50u003Cbru003E{ value: 120, done: false }u003Cbru003Estep 3: 20 50 120u003Cbru003E{ value: 1120, done: false }u003Cbru003E{ done: true }u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E从上述代码,我们可以看出,迭代器不仅仅是数据的迭代,还可以作为一个模式来组织相关的功能。(注:本示例来源《你不知道的JavaScript》下卷)u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E小节u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fdfic-imagehandleru002F46ae9372-226e-4c84-b504-9c062ee3a412″ img_width=”1023″ img_height=”683″ alt=”「ES6基础」迭代器(iterator)” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E今天的内容就到这里,迭代器是不是很神奇,好像如魔法一般,我们随意控制函数的中断与继续,丰富了我们解决问题的思路,让我们的代码看起来更加工程化和结构化,提高了代码的可读性和可理解性。u003Cu002Fpu003Eu003Cpu003E更多精彩内容,请微信关注“u003Cstrongu003E前端达人u003Cu002Fstrongu003E”公众号!u003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:「ES6基础」迭代器(iterator)

主题测试文章,只做测试使用。发布者:杀手梦三刀,转转请注明出处:http://www.cxybcw.com/10772.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code