好程序员大前端

好程序员分享JavaScript迭代器的含义

2019-07-19  本文已影响0人  ab6973df9221

  好程序员分享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方法主要向迭代器报告一个异常/错误,一般配合生成器使用。

  迭代器分类

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

·   内部迭代器:本身是函数,该函数内部定义好迭代规则,完全接受整个迭代过程,外部只需要一次调用。例如Array.prototype.forEach方法、jQuery.each都是内部迭代器。

·   外部迭代器:本身是函数,执行返回迭代对象,迭代下一个元素必须显式调用。使用forEach遍历,只可以一次性把数据全部拉取消耗,而迭代器可以用于以一次一步的方式控制行为,使得迭代过程更加灵活可控。

 好程序员官网:http://www.goodprogrammer.org

上一篇 下一篇

猜你喜欢

热点阅读