JavaScript迭代器的含义及用法

迭代器就是为实现对不同集合进行统一遍历操作的一种机制,只要给需要遍历的数据结构部署Iterator接口,通过调用该接口,或者使用消耗该接口的API实现遍历操作。

迭代器模式

在接触迭代器之前,一起先了解什么是迭代器模式,回想一下我们生活中的事例。我们在参观景区需要买门票的时候,售票员需要做的事情,他会对排队购票的每一个人依次进行售票,对普通成人,对学生,对儿童都依次售票。售票员需要按照一定的规则,一定顺序把参观人员一个不落的售完票,其实这个过程就是遍历,对应的就是计算机设计模式中的迭代器模式。迭代器模式,提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。

为什么要有迭代器

回忆在我们的javascript中,可遍历的结构以及方式有很多。JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set,这样就有了四种数据集合,而遍历这四种结构都有不同的方法。举个栗子,服务端提供数据给前端,前端进行数据可视化工作,对数据进行遍历展示使用的for,但是由于业务的变化,使得后端返回的数据结构发生变化,返回对象或者是set,map,导致前端遍历代码大量重写。而迭代器的目的就是要标准化迭代操作。

如何部署迭代器接口

ES6为迭代器引入了一个隐式的标准化接口。Javascript许多内建的数据结构,例如Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象都具备 Iterator 接口。可以通过在控制台打印一个Array实例,查看其原型上具有一个Symbol.iterator属性(Symbol.iterator其实是Symbol('Symbol.iterator')的简写,属性名是Symbol类型代表着这个属性的唯一以及不可重写覆盖),它就是迭代器函数,执行这个函数,就会返回一个迭代器对象。

虽然Javascript许多内建的数据结构已经实现了该接口,还有些结构是没有迭代器接口的(比如对象),那怎么办,我们需要写迭代器,那么就需要知道迭代器是如何工作的。下面代码实现的一个简单迭代器:

//迭代器就是一个函数,也叫迭代器生成函数 function Iterator(o){ let curIndex = 0; let next = () => { return { value: o[curIndex], done: o.length == ++curIndex } } //返回迭代对象,该对象有next方法 return { next } } let arr = [1,2] let oIt = Iterator(arr) oIt.next();//{value:1,done:false} oIt.next();//{value:2,done:false} oIt.next();// {value: undefined, done: true} oIt.next();// {value: undefined, done: true}

调用迭代器函数,返回一个对象,该对象就是迭代器对象,对象上拥有next方法,每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
next()迭代

在上面调用next方法的栗子中,需要注意的是:

在获得数组最后一位元素的时候,迭代器不会报告done:true,这时候需要再次调用next(),越过数组结尾的值,才能得到完成信号done:true。

通常情况下,在已经迭代完毕的迭代器对象上继续调用next方法会继续返回{value: undefined, done: true}而不会报错。

可选的return()和throw()

遍历器对象除了必须具有next方法,还可以具有可选的return方法和throw方法。

return方法被定义为向迭代器发送一个信号,表明不会在消费者中再提取出任何值。

Object.prototype[Symbol.iterator] = function () { let curIndex = 0; let next = () => { return { value: this[curIndex], done: this.length == curIndex++ } } return { next, return() { console.log('执行return啦') return {} } } } let obj = { 0: 'a', 1: 'b', 2: 'c' } //自动调用---遇到对迭代器消耗提前终止的条件 for (let item of obj) { if (item == 'c') { break } else { console.log(item) } } //自动调用---抛出异常 for (let item of obj) { if (item == 'c') { throw new Error('Errow') } else { console.log(item) } } //手动调用 let ot = obj[Symbol.iterator]() console.log(ot.return())

上面代码中,throw方法的执行可以在某种情况下自动被调用,也可以手动调用。throw方法主要向迭代器报告一个异常/错误,一般配合生成器使用。

迭代器分类

迭代器分为内部迭代器和外部迭代器。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/2c8781c41d831c008a099d571a44dd0d.html